Resolve TS snapshot filters by schema alias

This commit is contained in:
Meta Vens 2026-06-27 21:46:05 +01:00
commit 2738f4c2db
2 changed files with 85 additions and 2 deletions

View file

@ -159,7 +159,7 @@ export class CultNetDocumentRegistry {
const binding = this.#requireBinding(envelope.type);
const schemaId = schemaIdForEnvelope(envelope, binding);
if (requestedSchemaIds && !requestedSchemaIds.has(schemaId)) {
if (requestedSchemaIds && !schemaMatchesBinding(schemaId, binding, requestedSchemaIds)) {
continue;
}
@ -196,7 +196,7 @@ export class CultNetDocumentRegistry {
const binding = this.#requireBinding(envelope.type);
const schemaId = schemaIdForEnvelope(envelope, binding);
if (requestedSchemaIds && !requestedSchemaIds.has(schemaId)) {
if (requestedSchemaIds && !schemaMatchesBinding(schemaId, binding, requestedSchemaIds)) {
continue;
}
@ -340,6 +340,39 @@ function schemaIdForBinding(binding: CultNetDocumentBinding): string {
return binding.definition.schemaId ?? binding.definition.type;
}
function schemaMatchesBinding(
schemaId: string,
binding: CultNetDocumentBinding,
requestedSchemaIds: Set<string>,
): boolean {
if (requestedSchemaIds.has(schemaId)) {
return true;
}
const definition = binding.definition;
if (definition.schemaId && requestedSchemaIds.has(definition.schemaId)) {
return true;
}
if (definition.schemaName && requestedSchemaIds.has(definition.schemaName)) {
return true;
}
if (definition.compatibleSchemaIds?.some(candidate => requestedSchemaIds.has(candidate))) {
return true;
}
return Array.from(requestedSchemaIds)
.some(candidate => inferSchemaName(candidate) === definition.schemaName);
}
function inferSchemaName(schemaId: string): string | undefined {
const marker = schemaId.lastIndexOf(".v");
if (marker <= 0 || marker + 2 >= schemaId.length) {
return undefined;
}
const version = schemaId.slice(marker + 2);
return /^\d+$/u.test(version) ? schemaId.slice(0, marker) : undefined;
}
function schemaIdForEnvelope(
envelope: CultCacheEnvelope,
binding: CultNetDocumentBinding,

View file

@ -1266,6 +1266,56 @@ test("CultNet raw replication applies compatible foreign-schema snapshots", asyn
}
});
test("CultNet document registry filters snapshots by schema aliases", async () => {
const tempDir = mkdtempSync(join(tmpdir(), "cultnetts-alias-snapshot-"));
try {
const documentDefinition = defineDocumentType({
type: "cultnet.alias_note",
schemaId: "sha256:cultnet-alias-note",
schemaName: "cultnet.alias_note",
schemaVersion: "cultnet.alias_note.v1",
schema: z.object({
schema_version: z.string(),
noteId: z.string(),
body: z.string(),
}),
name: "noteId",
});
const registry = new CultNetDocumentRegistry([
defineCultNetDocumentBinding({ definition: documentDefinition }),
]);
const cache = CultCache.builder()
.withDocumentType(documentDefinition)
.withGenericStore(new SingleFileMessagePackBackingStore(join(tempDir, "alias.msgpack")))
.build();
await cache.put(documentDefinition, "note:alias", {
schema_version: "cultnet.alias_note.v1",
noteId: "note:alias",
body: "filtered by alias",
});
const typedSnapshot = registry.createSnapshotResponse(cache, "typed-alias", {
schemaVersion: "cultnet.snapshot_request.v0",
messageId: "typed-alias",
schemaIds: ["cultnet.alias_note.v1"],
});
const rawSnapshot = registry.createRawSnapshotResponse(cache, "raw-alias", {
schemaVersion: "cultnet.snapshot_request.v0",
messageId: "raw-alias",
schemaIds: ["cultnet.alias_note.v1"],
});
assert.equal(typedSnapshot.documents[0]?.schemaId, "sha256:cultnet-alias-note");
assert.equal(typedSnapshot.documents[0]?.recordKey, "note:alias");
assert.equal(rawSnapshot.documents[0]?.schemaId, "sha256:cultnet-alias-note");
assert.equal(rawSnapshot.documents[0]?.recordKey, "note:alias");
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
});
test("CultNet interop slot compatibility defaults missing trailing fields and rejects mismatched slots", () => {
const note: InteropNote = {
schemaVersion: INTEROP_SCHEMA_VERSION,