From 2cf1e07bac2cc93314db4476f1db267d59ce2d3b Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 30 Sep 2026 14:09:50 +0200 Subject: [PATCH] cultnet-ts: the registry sizes the reply that would serve a raw put CultNetDocumentRegistry.createRawSnapshotResponseForPut(put, messageId) returns the snapshot response that would serve the put's document alone: the envelope applyRawDocumentPutMessage stores, as createRawSnapshotResponse serves it. A server can judge a put by the size it would be served at. applyRawDocumentPutMessage and the new method share one envelope builder, and the served record comes from the snapshot path's own record builder, so the size and the stored record cannot drift. Co-Authored-By: Claude Opus 5.5 --- packages/cultnet-ts/src/replication.ts | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/cultnet-ts/src/replication.ts b/packages/cultnet-ts/src/replication.ts index 77dab70f..a2eda250 100644 --- a/packages/cultnet-ts/src/replication.ts +++ b/packages/cultnet-ts/src/replication.ts @@ -246,17 +246,46 @@ export class CultNetDocumentRegistry { cache: CultCache, message: CultNetDocumentPutRawMessage, ): Promise { + const { binding, envelope } = this.#rawPutEnvelope(message); + return cache.putEnvelope(binding.definition, envelope); + } + + /** + * The snapshot response that would serve this raw put's document alone: the + * record `applyRawDocumentPutMessage` would store, as `createRawSnapshotResponse` + * would serve it. A server uses it to judge a put by the size it would be + * served at. Throws, as the put would, when no binding accepts the document. + */ + createRawSnapshotResponseForPut( + message: CultNetDocumentPutRawMessage, + messageId: string, + ): CultNetSnapshotResponseRawMessage { + const { envelope } = this.#rawPutEnvelope(message); + return { + schemaVersion: "cultnet.snapshot_response_raw.v0", + messageId, + documents: [this.#createRawDocumentRecord(envelope)], + }; + } + + #rawPutEnvelope(message: CultNetDocumentPutRawMessage): { + binding: CultNetDocumentBinding; + envelope: CultCacheEnvelope; + } { const resolution = this.#resolveRawDocumentBinding(message.document); const { binding } = resolution; - return cache.putEnvelope(binding.definition, { - key: message.document.recordKey, - type: binding.definition.type, - schemaId: resolution.preserveIncomingSchemaId - ? message.document.schemaId - : schemaIdForBinding(binding), - payload: new Uint8Array(message.document.payload), - storedAt: message.document.storedAt, - }); + return { + binding, + envelope: { + key: message.document.recordKey, + type: binding.definition.type, + schemaId: resolution.preserveIncomingSchemaId + ? message.document.schemaId + : schemaIdForBinding(binding), + payload: new Uint8Array(message.document.payload), + storedAt: message.document.storedAt, + }, + }; } async applySnapshotResponse(