From da48d58df73e519eae35d3922d9e07636f626aae Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 16:18:08 +0100 Subject: [PATCH] Add TS CultMesh catalog factories --- docs/cultnet-transport-parity.md | 6 +- docs/runtime-parity-scope.md | 2 +- packages/cultmesh-ts/README.md | 21 +++++++ packages/cultmesh-ts/src/index.ts | 26 ++++++++ packages/cultmesh-ts/test/cultmesh.test.ts | 73 ++++++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) diff --git a/docs/cultnet-transport-parity.md b/docs/cultnet-transport-parity.md index 66c07c37..3417448b 100644 --- a/docs/cultnet-transport-parity.md +++ b/docs/cultnet-transport-parity.md @@ -312,7 +312,11 @@ Current progress: ergonomics as the other runtimes: sorted local views, direct `get(...)` lookup, unsubscribe-able `watch(...)` callbacks for Verse and peer catalogs, watch callbacks for authority leases and stream declarations/frame cursors, - plus lease listing and lookup beside the authority check. + plus lease listing and lookup beside the authority check. The branded + `CultMesh` facade also creates schema catalogs, built-in schema catalogs, and + shard catalogs by delegating to the existing `cultnet-ts` owners, so callers + do not have to leave the CultMesh entrypoint for schema discovery or shard + topology. - TypeScript now also has a `CultNetSchemaCatalog` for remote schema descriptors plus `CultNetPeer` request/fetch/sync helpers for `cultnet.schema_catalog_request.v0` / `cultnet.schema_catalog_response.v0`. diff --git a/docs/runtime-parity-scope.md b/docs/runtime-parity-scope.md index 976c0023..296809ab 100644 --- a/docs/runtime-parity-scope.md +++ b/docs/runtime-parity-scope.md @@ -32,7 +32,7 @@ CultMesh. | --- | --- | --- | --- | | C# | Reference implementation and production game/server substrate | CultCache typed documents and SoA, CultNet schema-v0, LiteNetLib production path with adapter profile, shared transport profiles, shared RUDP socket transport, RUDP reconnect policy/controller and reconnect-loop helper, LiteNetLib client reconnect-controller adoption, schema-client transport port for service request/response clients, CultMesh node/session/catalog/stream/simulation surfaces | No need to abandon LiteNetLib before the CultNet adapter fully owns production service adoption | | Python | Package runtime, local daemon endpoint, raw interop peer, typed state participant | CultCache/CultNet/CultMesh package surface, schema catalogs, shard catalogs/logs, schema-transport factory for raw client request/subscription flows, local TCP CultMesh server, session/prediction/simulation fact helpers, authority leases, streams, RUDP codec/session/socket transport, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence, benchmark evidence against C# cache paths | Universal C# throughput, full C# daemon/server stack, and RUDP as the only public service pipe | -| TypeScript | Node/browser-adjacent projection and interop runtime | CultCache parity tests, CultNet schema-v0 peer, advertised schema/shard catalog wire contracts plus request/sync helpers, transport profiles, TCP-framed transport port and peer helper, shared RUDP socket transport, RUDP reconnect policy/controller, RUDP reconnect-loop helper, local CultMesh node/catalog/authority/stream ergonomics | Full C# game session/server stack | +| TypeScript | Node/browser-adjacent projection and interop runtime | CultCache parity tests, CultNet schema-v0 peer, advertised schema/shard catalog wire contracts plus request/sync helpers, transport profiles, TCP-framed transport port and peer helper, shared RUDP socket transport, RUDP reconnect policy/controller, RUDP reconnect-loop helper, branded CultMesh node/schema/shard/Verse/peer/authority/stream ergonomics | Full C# game session/server stack | | Rust | Low-level cache/transport/runtime substrate | CultCache/CultNet interop lane, schema and shard catalog helpers, transport profiles, TCP-framed port, shared RUDP codec/session/socket transport, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence, small CultMesh facade for peer cards, authority leases, RUDP endpoint parsing, and peer-card RUDP client construction | Full CultMesh game-session/server facade, stream catalog, or production game-session server | | Kotlin | JVM/Android client substrate | Typed MessagePack cache, built-in schema catalog advertisement, schema/shard catalogs with shared schema-message transport fetch/sync helpers, authority leases, stream catalog, branded `CultMesh` facade, WebSocket transport profile/connection helper, RUDP client/server sugar, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence | C# SoA store, full C# server daemon, or Unity hot-loop memory layout | diff --git a/packages/cultmesh-ts/README.md b/packages/cultmesh-ts/README.md index f1128034..5a925197 100644 --- a/packages/cultmesh-ts/README.md +++ b/packages/cultmesh-ts/README.md @@ -46,8 +46,29 @@ leases.upsert({ expiresAt: new Date(Date.now() + 60_000), }); stopWatchingLeases(); + +const schemas = CultMesh.createBuiltInSchemaCatalog(); +const shardCatalogRequest = schemas.get("cultnet.shard_catalog_request.v0"); + +const shards = CultMesh.createShardCatalog(); +shards.upsert({ + shardId: "notes-a", + ownerRuntimeId: "ts-peer", + epoch: 1, + schemaIds: ["cultmesh.note.v0"], + keyPrefix: "note:", +}); + +const noteShards = shards.list({ + schemaIds: ["cultmesh.note.v0"], + recordKeys: ["note:intro"], +}); ``` +Schema and shard catalog factories delegate to `cultnet-ts`; the branded +`CultMesh` entrypoint does not create a second owner for schema discovery or +topology truth. + ## Streaming Mode CultMesh streaming mode is for audio, video, tensor, and opaque byte frames that diff --git a/packages/cultmesh-ts/src/index.ts b/packages/cultmesh-ts/src/index.ts index 0be40c15..f285d41d 100644 --- a/packages/cultmesh-ts/src/index.ts +++ b/packages/cultmesh-ts/src/index.ts @@ -7,8 +7,12 @@ import { } from "cultcache-ts"; import { CultNetDocumentRegistry, + CultNetSchemaCatalog, + CultNetShardCatalog, + cultNetBuiltinSchemaRegistry, defineCultNetDocumentBinding, type CultNetDocumentBinding, + type CultNetSchemaCatalogOptions, } from "cultnet-ts"; export interface CultMeshNodeOptions { @@ -470,6 +474,28 @@ export class CultMesh { public static createStreamCatalog(): CultMeshStreamCatalog { return new CultMeshStreamCatalog(); } + + public static createSchemaCatalog(): CultNetSchemaCatalog { + return new CultNetSchemaCatalog(); + } + + public static createBuiltInSchemaCatalog( + options: CultNetSchemaCatalogOptions = {}, + ): CultNetSchemaCatalog { + const catalog = new CultNetSchemaCatalog(); + catalog.applyResponse(cultNetBuiltinSchemaRegistry.createCatalogResponse({ + schemaVersion: "cultnet.schema_catalog_request.v0", + messageId: "cultmesh-ts-builtins", + includeSchemaJson: options.includeSchemaJson, + schemaIds: options.schemaIds ? [...options.schemaIds] : undefined, + kinds: options.kinds ? [...options.kinds] : undefined, + })); + return catalog; + } + + public static createShardCatalog(): CultNetShardCatalog { + return new CultNetShardCatalog(); + } } function requireNonEmpty(value: string, name: string): void { diff --git a/packages/cultmesh-ts/test/cultmesh.test.ts b/packages/cultmesh-ts/test/cultmesh.test.ts index 2c962445..8d3af235 100644 --- a/packages/cultmesh-ts/test/cultmesh.test.ts +++ b/packages/cultmesh-ts/test/cultmesh.test.ts @@ -115,6 +115,79 @@ test("CultMesh TS local Verse catalog exposes sorted views and watch ergonomics" ); }); +test("CultMesh TS branded facade exposes schema and shard catalogs", () => { + const schemaCatalog = CultMesh.createSchemaCatalog(); + const schemaUpdates: string[] = []; + const unsubscribeSchema = schemaCatalog.watch((descriptor) => { + schemaUpdates.push(descriptor.schemaId); + }); + schemaCatalog.upsert({ + schemaId: "cultmesh.ts.note.v1", + kind: "document_payload", + documentType: "cultmesh.ts-note", + wireContracts: ["cultnet.schema.v0"], + contentHash: "note-hash", + }); + unsubscribeSchema(); + schemaCatalog.upsert({ + schemaId: "cultmesh.ts.other.v1", + kind: "document_payload", + documentType: "cultmesh.ts-other", + wireContracts: ["cultnet.schema.v0"], + contentHash: "other-hash", + }); + + assert.deepEqual(schemaUpdates, ["cultmesh.ts.note.v1"]); + assert.equal( + schemaCatalog.get("cultmesh.ts.note.v1")?.documentType, + "cultmesh.ts-note", + ); + + const builtIns = CultMesh.createBuiltInSchemaCatalog(); + assert.equal( + builtIns.get("cultnet.shard_catalog_request.v0")?.schemaVersion, + "cultnet.shard_catalog_request.v0", + ); + assert.equal( + builtIns.get("cultnet.shard_catalog_response.v0")?.kind, + "wire_message", + ); + assert.equal( + builtIns.get( + "https://github.com/GameCult/cultnet-ts/contracts/cultnet.transport-profile.schema.json", + )?.schemaVersion, + "cultnet.transport_profile.v0", + ); + + const shardCatalog = CultMesh.createShardCatalog(); + const shardUpdates: string[] = []; + const unsubscribeShard = shardCatalog.watch((descriptor) => { + shardUpdates.push(descriptor.shardId); + }); + shardCatalog.upsert({ + shardId: "notes-a", + ownerRuntimeId: "ts-runtime", + epoch: 7, + schemaIds: ["cultmesh.ts.note.v1"], + keyPrefix: "note:", + }); + unsubscribeShard(); + shardCatalog.upsert({ + shardId: "notes-b", + ownerRuntimeId: "ts-runtime", + epoch: 8, + }); + + assert.deepEqual(shardUpdates, ["notes-a"]); + assert.equal(shardCatalog.get("notes-a")?.ownerRuntimeId, "ts-runtime"); + assert.deepEqual( + shardCatalog + .list({ schemaIds: ["cultmesh.ts.note.v1"], recordKeys: ["note:1"] }) + .map((shard) => shard.shardId), + ["notes-a"], + ); +}); + test("CultMesh TS negotiates streaming frame body transports explicitly", () => { const streams = CultMesh.createStreamCatalog(); const streamUpdates: string[] = [];