From aeb7a96bd3be822f0af77a7d5d77410ee24e0387 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 27 Jun 2026 21:56:02 +0100 Subject: [PATCH] Resolve TS typed document messages by schema alias --- packages/cultnet-ts/src/replication.ts | 13 +++++- packages/cultnet-ts/test/cultnet.test.ts | 57 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/cultnet-ts/src/replication.ts b/packages/cultnet-ts/src/replication.ts index f53f7c26..d5706bea 100644 --- a/packages/cultnet-ts/src/replication.ts +++ b/packages/cultnet-ts/src/replication.ts @@ -57,7 +57,15 @@ export class CultNetDocumentRegistry { } getBySchemaId(schemaId: string): CultNetDocumentBinding | undefined { - return this.#schemaBindings.get(schemaId); + const exact = this.#schemaBindings.get(schemaId); + if (exact) { + return exact; + } + + const requestedSchemaIds = new Set([schemaId]); + return this.#uniqueBindings().find(binding => + schemaMatchesBinding(schemaIdForBinding(binding), binding, requestedSchemaIds), + ); } createDocumentPutMessage( @@ -356,6 +364,9 @@ function schemaMatchesBinding( if (definition.schemaName && requestedSchemaIds.has(definition.schemaName)) { return true; } + if (definition.schemaVersion && requestedSchemaIds.has(definition.schemaVersion)) { + return true; + } if (definition.compatibleSchemaIds?.some(candidate => requestedSchemaIds.has(candidate))) { return true; } diff --git a/packages/cultnet-ts/test/cultnet.test.ts b/packages/cultnet-ts/test/cultnet.test.ts index bd9d79f3..85cafcf6 100644 --- a/packages/cultnet-ts/test/cultnet.test.ts +++ b/packages/cultnet-ts/test/cultnet.test.ts @@ -1316,6 +1316,63 @@ test("CultNet document registry filters snapshots by schema aliases", async () = } }); +test("CultNet document registry applies typed put and delete messages by schema alias", async () => { + const tempDir = mkdtempSync(join(tmpdir(), "cultnetts-alias-typed-")); + + try { + const documentDefinition = defineDocumentType({ + type: "cultnet.typed_alias_note", + schemaId: "sha256:cultnet-typed-alias-note", + schemaName: "cultnet.typed_alias_note", + schemaVersion: "cultnet.typed_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, "typed-alias.msgpack"))) + .build(); + + await registry.applyDocumentPutMessage(cache, { + schemaVersion: "cultnet.document_put.v0", + messageId: "typed-alias-put", + document: { + schemaId: "cultnet.typed_alias_note.v1", + recordKey: "note:typed-alias", + storedAt: "2026-06-27T00:00:00.000Z", + payload: { + schema_version: "cultnet.typed_alias_note.v1", + noteId: "note:typed-alias", + body: "typed alias applied", + }, + }, + }); + + assert.equal( + cache.getRequired(documentDefinition, "note:typed-alias").body, + "typed alias applied", + ); + assert.equal(registry.getBySchemaId("cultnet.typed_alias_note.v1")?.definition.type, documentDefinition.type); + + assert.equal(await registry.applyDocumentDeleteMessage(cache, { + schemaVersion: "cultnet.document_delete.v0", + messageId: "typed-alias-delete", + schemaId: "cultnet.typed_alias_note.v1", + recordKey: "note:typed-alias", + }), true); + assert.equal(cache.get(documentDefinition, "note:typed-alias"), undefined); + } 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,