cultnet-ts: drop unset optional keys from createDocumentPutMessage #13

Merged
libby merged 1 commit from libby/cultnet-ts-put-nil-keys into main 2026-11-07 11:26:57 +00:00
2 changed files with 24 additions and 4 deletions

View file

@ -127,10 +127,11 @@ export class CultNetDocumentRegistry {
recordKey: documentKey,
storedAt: options.storedAt ?? new Date().toISOString(),
payload: parsed,
sourceRuntimeId: options.sourceRuntimeId,
sourceAgentId: options.sourceAgentId,
sourceRole: options.sourceRole,
tags: options.tags,
// Absent optional fields must be omitted, not set to undefined: MessagePack encodes undefined as nil and the peer rejects it.
...(options.sourceRuntimeId !== undefined && { sourceRuntimeId: options.sourceRuntimeId }),
...(options.sourceAgentId !== undefined && { sourceAgentId: options.sourceAgentId }),
...(options.sourceRole !== undefined && { sourceRole: options.sourceRole }),
...(options.tags !== undefined && { tags: options.tags }),
},
};
}

View file

@ -3059,3 +3059,22 @@ test("RUDP operation service admits a restarted client on the same address", asy
await server.close();
}
});
// MessagePack encodes undefined as nil, and a peer rejects a document whose optional source fields are nil.
test("document put message omits unset optional keys so the encoded wire has no nil", () => {
const definition = defineDocumentType({ type: "putnil.doc", schema: z.object({ body: z.string() }) });
const binding = defineCultNetDocumentBinding({ definition });
const registry = new CultNetDocumentRegistry([binding]);
const bare = registry.createDocumentPutMessage(binding, "m", "k", { body: "x" });
assert.deepEqual(Object.keys(bare.document).sort(), ["payload", "recordKey", "schemaId", "storedAt"]);
const decodedBare = decodeMsgpack(encode(bare)) as { document: Record<string, unknown> };
assert.ok(Object.values(decodedBare.document).every(value => value !== null), "no nil values on the wire");
assert.ok(!("sourceRuntimeId" in decodedBare.document));
const stamped = registry.createDocumentPutMessage(binding, "m", "k", { body: "x" },
{ sourceRuntimeId: "r", sourceAgentId: "a", sourceRole: "role", tags: ["t"] });
assert.deepEqual(decodeMsgpack(encode(stamped)), stamped);
assert.equal(stamped.document.sourceRuntimeId, "r");
assert.deepEqual(stamped.document.tags, ["t"]);
});