npm: scope cultnet-ts, cultmesh-ts, cultmesh-browser under @gamecult #28

Merged
libby merged 1 commit from libby/scope-npm-packages into main 2026-11-07 16:25:15 +00:00
55 changed files with 142 additions and 131 deletions

View file

@ -284,7 +284,7 @@ public static class CultNetFirstCall
Source: [`packages/cultnet-ts/test/first-call.test.ts`](../../packages/cultnet-ts/test/first-call.test.ts), run by
`npm test` in `packages/cultnet-ts` (build `packages/cultcache-ts` first: `npm run build` there). The test imports
from `../src` where the block reads `cultnet-ts`. More: `packages/cultnet-ts/README.md`.
from `../src` where the block reads `@gamecult/cultnet-ts`. More: `packages/cultnet-ts/README.md`.
```ts
import { createServer, connect } from "node:net";
@ -294,7 +294,7 @@ import {
CultNetDocumentRegistry,
createTcpFramedCultNetPeer,
defineCultNetDocumentBinding,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
const noteDocument = defineDocumentType({
type: "note",

View file

@ -9,7 +9,7 @@ leave because both lease the same document.
| Piece | Status |
| --- | --- |
| Two clients, real WebSockets, real `cultmesh-browser` client, one arena: join, see each other, move, leave | **tested** (`arena.test.ts`) |
| Two clients, real WebSockets, real `@gamecult/cultmesh-browser` client, one arena: join, see each other, move, leave | **tested** (`arena.test.ts`) |
| Edge clamp, and the source below matching the files that ran | **tested** |
| Browser bundle builds and the page is served | **tested** (HTTP fetch only) |
| The page running in a real browser, two windows | **not yet**: no browser on the host that built this recipe |
@ -19,7 +19,7 @@ leave because both lease the same document.
| Dropped players: a closed tab that never sent `leave` stays in the arena | **not yet**: operations do not carry a connection identity to the authority |
| `CultCache` in the browser | **not yet**: the `@gamecult/cultcache-ts` entry imports Node built-ins, so the client keeps the decoded document in a variable and only `./document` is imported |
No `cultmesh-ts` or `cultnet-ts` TypeScript authority server exists. The server below is the CultNet
No `@gamecult/cultmesh-ts` or `@gamecult/cultnet-ts` TypeScript authority server exists. The server below is the CultNet
message handling a provider owes, written in this recipe with the `ws` package.
## Run it
@ -61,8 +61,8 @@ export const arenaSize = 100;
import { createServer, type RequestListener, type Server } from "node:http";
import { CultCache } from "@gamecult/cultcache-ts";
import { defineDocumentRegistry } from "@gamecult/cultcache-ts/document";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { encodeCultNetMessageForWire, parseCultNetMessage, type CultNetMessage } from "cultnet-ts/contracts";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import { encodeCultNetMessageForWire, parseCultNetMessage, type CultNetMessage } from "@gamecult/cultnet-ts/contracts";
import { WebSocketServer, type WebSocket } from "ws";
import { arenaDocument, arenaKey, arenaSize, authorityRuntimeId, verseId, type ArenaState } from "./schema.ts";
@ -163,7 +163,7 @@ export async function startArena(port = 0, host = "127.0.0.1", onRequest?: Reque
## client.ts
```ts
import { CultMeshBrowserClient, CultMeshBrowserOdinRendezvous, decodeCultNetOperationPayload, decodeCultNetPayload } from "cultmesh-browser";
import { CultMeshBrowserClient, CultMeshBrowserOdinRendezvous, decodeCultNetOperationPayload, decodeCultNetPayload } from "@gamecult/cultmesh-browser";
import { arenaDocument, arenaKey, authorityRuntimeId, verseId, type ArenaState } from "./schema.ts";
// Runs unchanged in a browser or in Node 22+: both have a global WebSocket.

View file

@ -1,4 +1,4 @@
import { CultMeshBrowserClient, CultMeshBrowserOdinRendezvous, decodeCultNetOperationPayload, decodeCultNetPayload } from "cultmesh-browser";
import { CultMeshBrowserClient, CultMeshBrowserOdinRendezvous, decodeCultNetOperationPayload, decodeCultNetPayload } from "@gamecult/cultmesh-browser";
import { arenaDocument, arenaKey, authorityRuntimeId, verseId, type ArenaState } from "./schema.ts";
// Runs unchanged in a browser or in Node 22+: both have a global WebSocket.

View file

@ -1,8 +1,8 @@
import { createServer, type RequestListener, type Server } from "node:http";
import { CultCache } from "@gamecult/cultcache-ts";
import { defineDocumentRegistry } from "@gamecult/cultcache-ts/document";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { encodeCultNetMessageForWire, parseCultNetMessage, type CultNetMessage } from "cultnet-ts/contracts";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import { encodeCultNetMessageForWire, parseCultNetMessage, type CultNetMessage } from "@gamecult/cultnet-ts/contracts";
import { WebSocketServer, type WebSocket } from "ws";
import { arenaDocument, arenaKey, arenaSize, authorityRuntimeId, verseId, type ArenaState } from "./schema.ts";

33
package-lock.json generated
View file

@ -482,6 +482,18 @@
"resolved": "packages/cultcache-ts",
"link": true
},
"node_modules/@gamecult/cultmesh-browser": {
"resolved": "packages/cultmesh-browser",
"link": true
},
"node_modules/@gamecult/cultmesh-ts": {
"resolved": "packages/cultmesh-ts",
"link": true
},
"node_modules/@gamecult/cultnet-ts": {
"resolved": "packages/cultnet-ts",
"link": true
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
@ -865,18 +877,6 @@
"dev": true,
"license": "Python-2.0"
},
"node_modules/cultmesh-browser": {
"resolved": "packages/cultmesh-browser",
"link": true
},
"node_modules/cultmesh-ts": {
"resolved": "packages/cultmesh-ts",
"link": true
},
"node_modules/cultnet-ts": {
"resolved": "packages/cultnet-ts",
"link": true
},
"node_modules/esbuild": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
@ -1200,15 +1200,16 @@
}
},
"packages/cultmesh-browser": {
"name": "@gamecult/cultmesh-browser",
"version": "0.4.0",
"license": "MIT",
"dependencies": {
"@gamecult/cultcache-ts": "^0.14.0",
"cultnet-ts": "^0.3.0"
"@gamecult/cultnet-ts": "^0.3.0"
},
"devDependencies": {
"@gamecult/cultmesh-ts": "^0.2.0",
"@types/node": "^24.0.0",
"cultmesh-ts": "^0.2.0",
"typescript": "^5.9.0"
},
"engines": {
@ -1216,11 +1217,12 @@
}
},
"packages/cultmesh-ts": {
"name": "@gamecult/cultmesh-ts",
"version": "0.2.0",
"license": "MIT",
"dependencies": {
"@gamecult/cultcache-ts": "^0.14.0",
"cultnet-ts": "^0.3.0",
"@gamecult/cultnet-ts": "^0.3.0",
"koffi": "3.3.0"
},
"devDependencies": {
@ -1233,6 +1235,7 @@
}
},
"packages/cultnet-ts": {
"name": "@gamecult/cultnet-ts",
"version": "0.3.0",
"license": "MIT",
"dependencies": {

View file

@ -1,8 +1,14 @@
# Changelog
All notable changes to `cultmesh-browser` are recorded here, following
All notable changes to `@gamecult/cultmesh-browser` are recorded here, following
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## Unreleased
### Breaking
- The package is renamed `cultmesh-browser` to `@gamecult/cultmesh-browser` (the repo already scopes `@gamecult/cultcache-ts`), so the unscoped name cannot be claimed by someone else. Import from `@gamecult/cultmesh-browser` and its subpaths; the API is unchanged. Depends on `@gamecult/cultnet-ts`; the interop test uses `@gamecult/cultmesh-ts`.
## [0.4.0]
### Added

View file

@ -1,6 +1,6 @@
# CultMesh Browser
`cultmesh-browser` is the browser-safe CultMesh client boundary. It owns one
`@gamecult/cultmesh-browser` is the browser-safe CultMesh client boundary. It owns one
WebSocket session for a stable Verse/authority-runtime target, retains explicit raw
document leases across route replacement, and correlates typed CultNet
operations with provider receipts.
@ -95,7 +95,7 @@ WebCrypto `crypto.subtle`; without WebCrypto it fails with
endpoint CultMesh resolved is the only host the reader trusts.
```ts
import { fetchVerifiedContent } from "cultmesh-browser";
import { fetchVerifiedContent } from "@gamecult/cultmesh-browser";
const bytes = await fetchVerifiedContent(endpoint, manifest, {
signal: AbortSignal.timeout(30_000),

View file

@ -1,5 +1,5 @@
{
"name": "cultmesh-browser",
"name": "@gamecult/cultmesh-browser",
"version": "0.4.0",
"description": "Browser-safe CultMesh document leases and typed operations over CultNet WebSocket transport.",
"type": "module",
@ -42,11 +42,11 @@
},
"dependencies": {
"@gamecult/cultcache-ts": "^0.14.0",
"cultnet-ts": "^0.3.0"
"@gamecult/cultnet-ts": "^0.3.0"
},
"devDependencies": {
"@types/node": "^24.0.0",
"cultmesh-ts": "^0.2.0",
"@gamecult/cultmesh-ts": "^0.2.0",
"typescript": "^5.9.0"
}
}

View file

@ -3,8 +3,8 @@
// The response's declared length and coding are never consulted (Fetch decodes codings; the bytes received are the evidence).
// Hashing, hex rules, record keys, the loopback rule and manifest validation belong to cultnet-ts and are only called here.
import { EMPTY_SHA256, chunkRecordKey, parseSha256Hex, sha256Hex, validateManifest, type CultMeshCdnArtifactManifest } from "cultnet-ts/content";
import { isLoopbackEndpoint } from "cultnet-ts/authority";
import { EMPTY_SHA256, chunkRecordKey, parseSha256Hex, sha256Hex, validateManifest, type CultMeshCdnArtifactManifest } from "@gamecult/cultnet-ts/content";
import { isLoopbackEndpoint } from "@gamecult/cultnet-ts/authority";
export type CultMeshContentErrorCode =
| "manifest-invalid" | "too-large" | "insecure-endpoint" | "webcrypto-unavailable" | "network"

View file

@ -1,6 +1,6 @@
import { decodeDocumentPayload } from "@gamecult/cultcache-ts/document";
import type { AnyCultCacheDocumentDefinition, CultCacheDocumentValue } from "@gamecult/cultcache-ts";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
encodeCultNetMessageForWire,
parseCultNetMessage,
@ -16,7 +16,7 @@ import {
type CultMeshVerseCatalogResponseMessage,
type CultMeshSessionAcceptedMessage,
type CultMeshSessionOpenMessage,
} from "cultnet-ts/contracts";
} from "@gamecult/cultnet-ts/contracts";
import {
base64ToBytes,
bytesToBase64,
@ -31,7 +31,7 @@ import {
type CultMeshAuthorityTrustMode,
type CultMeshAuthorityTrustPolicy,
type CultMeshP256PublicKey,
} from "cultnet-ts/authority";
} from "@gamecult/cultnet-ts/authority";
export * from "./content.js";
export type CultMeshBrowserIdentity = CultMeshAuthorityIdentity;

View file

@ -11,7 +11,7 @@ import { gzipSync } from "node:zlib";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { build } from "esbuild";
import { EMPTY_SHA256, packContent, sha256Hex, type CultMeshCdnArtifactManifest } from "cultnet-ts/content";
import { EMPTY_SHA256, packContent, sha256Hex, type CultMeshCdnArtifactManifest } from "@gamecult/cultnet-ts/content";
import { CultMeshContentError, fetchVerifiedChunk, fetchVerifiedContent, type CultMeshContentErrorCode } from "../src/content.js";

View file

@ -2,7 +2,7 @@ import assert from "node:assert/strict";
import test from "node:test";
import { codecs, defineDocumentType, documentPayloadFromBytes } from "@gamecult/cultcache-ts";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
encodeCultNetMessageForWire,
parseCultNetMessage,
@ -12,7 +12,7 @@ import {
type CultNetRawDocumentRecord,
type CultMeshVerseCatalogResponseMessage,
type CultMeshSessionOpenMessage,
} from "cultnet-ts/contracts";
} from "@gamecult/cultnet-ts/contracts";
import {
CultMeshBrowserClient,

View file

@ -5,8 +5,8 @@ import { readFileSync } from "node:fs";
import { createServer } from "node:https";
import type { AddressInfo } from "node:net";
import { createInterface } from "node:readline";
import { chunkRecordKey } from "cultnet-ts/content";
import { createContentChunkHandler, createDirectoryChunkStore, toNodeListener, type ContentChunkAnswer, type ContentChunkRequest } from "cultmesh-ts/content";
import { chunkRecordKey } from "@gamecult/cultnet-ts/content";
import { createContentChunkHandler, createDirectoryChunkStore, toNodeListener, type ContentChunkAnswer, type ContentChunkRequest } from "@gamecult/cultmesh-ts/content";
const [storeDir, corruptDir, keyPem, certPem, escapeKey] = process.argv.slice(2) as [string, string, string, string, string];
const say = (line: object): void => { process.stdout.write(`${JSON.stringify(line)}\n`); };

View file

@ -3,7 +3,7 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { decodeManifest } from "cultnet-ts/content";
import { decodeManifest } from "@gamecult/cultnet-ts/content";
import { fetchVerifiedChunk, fetchVerifiedContent, CultMeshContentError } from "../../src/content.js";
const [endpointsJson, manifestsDir, only] = process.argv.slice(2) as [string, string, string | undefined];

View file

@ -12,7 +12,7 @@ import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { EMPTY_SHA256, encodeManifest, packContent } from "cultnet-ts/content";
import { EMPTY_SHA256, encodeManifest, packContent } from "@gamecult/cultnet-ts/content";
const execFileAsync = promisify(execFile);
const here = dirname(fileURLToPath(import.meta.url)); // dist-test/test/interop

View file

@ -2,8 +2,8 @@ import assert from "node:assert/strict";
import test from "node:test";
import { documentPayloadBytes, documentPayloadFromBytes, type DocumentPayload } from "@gamecult/cultcache-ts";
import { decodeWireValue } from "cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "cultnet-ts/contracts";
import { decodeWireValue } from "@gamecult/cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "@gamecult/cultnet-ts/contracts";
import { decodeCultNetPayload } from "../src/index.js";

View file

@ -7,7 +7,7 @@ game sessions with prediction reconciliation, committed simulation facts, the
local TCP/RUDP server, and a package daemon.
It depends on `cultcache-py` and `cultnet-py`, mirroring `cultmesh-rs` and
`cultmesh-ts`. The Verse catalog, peer exchange, authority lease, stream
`@gamecult/cultmesh-ts`. The Verse catalog, peer exchange, authority lease, stream
negotiation, and simulation fact contracts are defined in
`cultnet_py.cultmesh_contracts` and re-exported here.

View file

@ -1,12 +1,13 @@
# Changelog
All notable changes to `cultmesh-ts` are recorded here, following
All notable changes to `@gamecult/cultmesh-ts` are recorded here, following
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## Unreleased
### Breaking
- The package is renamed `cultmesh-ts` to `@gamecult/cultmesh-ts` (the repo already scopes `@gamecult/cultcache-ts`), so the unscoped name cannot be claimed by someone else. Import from `@gamecult/cultmesh-ts` and its subpaths; the API is unchanged. Depends on `@gamecult/cultnet-ts`.
- `asSchemaAlias` is removed from `CultMeshDocumentHandle`, `CultMeshCollectionHandle` and their bound forms. Read the handle by the schema you mean.
- `CultMeshDocumentCatalog` (`document`, `tryDocument`, `latest`, `replace`, `submitPrediction`, `authoritativeWriter`, `predictionWriter`, `watch`) and `CultMeshCollectionCatalog` (`collection`, `tryCollection`, `latest`, `watchChanges`) take `<TDocument = never, TSchema>` and yield `CultMeshCatalogValue<TDocument, TSchema>`: an explicit type argument wins, a CultCache document definition names its own value type, and a bare schema descriptor yields `unknown`. A value type is no longer inferred from a value or an expected result. The `parse` option is removed from these calls, and a lookup by type answers with that type's handle or none, not an alias of a compatible schema.
- Adding a document or collection to a catalog under a schema id or schema name another type already holds is refused with `SchemaConflictError`.

View file

@ -1,6 +1,6 @@
# CultMesh TS
`cultmesh-ts` is the TypeScript projection of CultMesh contracts for local
`@gamecult/cultmesh-ts` is the TypeScript projection of CultMesh contracts for local
tools and browser-adjacent runtimes. The C# `GameCult.Mesh` package owns the
native CultMesh stream implementation used by Mimir and Fensalir; this package
mirrors the typed declaration and negotiation shapes without owning the hot
@ -21,7 +21,7 @@ WASM route. Runtime code can ask for a whole XY rect, then adapt it to the
current wire schema at the edge:
```ts
import { CultMesh } from "cultmesh-ts";
import { CultMesh } from "@gamecult/cultmesh-ts";
const viewport = CultMesh.rectFromBounds(-500, -300, 500, 300);
const request = CultMesh.viewportRequest(viewport, [3, 8]);
@ -215,7 +215,7 @@ const stopFeed = viewportFeed.watch(request, "browser-starfire", render);
```
```ts
import { CultMesh } from "cultmesh-ts";
import { CultMesh } from "@gamecult/cultmesh-ts";
const verses = CultMesh.createVerseCatalog<{ verseId: string; label: string }>();
const stopWatching = verses.watch((verse) => {
@ -274,7 +274,7 @@ const noteShards = shards.list({
});
```
Schema and shard catalog factories delegate to `cultnet-ts`; the branded
Schema and shard catalog factories delegate to `@gamecult/cultnet-ts`; the branded
`CultMesh` entrypoint does not create a second owner for schema discovery or
topology truth.
@ -290,7 +290,7 @@ command transactions; the injected transport owns CultNet wire details.
import {
CultMeshProviderRudpTransport,
CultMeshProviderSession,
} from "cultmesh-ts";
} from "@gamecult/cultmesh-ts";
const odinTransport = new CultMeshProviderRudpTransport({
endpoint: "rudp://127.0.0.1:17871",
@ -347,7 +347,7 @@ evidence for the requested provider identity.
Node runtimes can build the shared CultNet reliable-UDP transport from the
branded CultMesh entrypoint while the transport semantics stay owned by
`cultnet-ts`:
`@gamecult/cultnet-ts`:
```ts
const server = await CultMesh.createRudpServer("ts-server", 0x1020_3040);
@ -472,7 +472,7 @@ The split is deliberate:
The mirrored API shape is `CultMeshStreamCatalog`:
```ts
import { CultMesh } from "cultmesh-ts";
import { CultMesh } from "@gamecult/cultmesh-ts";
const streams = CultMesh.createStreamCatalog();
const stopWatchingStreams = streams.watch((stream) => {
@ -533,7 +533,7 @@ nonlocal fallback.
## Content Chunks over HTTP(S)
`cultmesh-ts/content` answers the C# HTTPS connector's request, `GET` or `HEAD`
`@gamecult/cultmesh-ts/content` answers the C# HTTPS connector's request, `GET` or `HEAD`
`<mount>?chunkHash=<hex>&recordKey=<key>`. The mount path is the host's; the
handler reads only the query. Every chunk is hashed against its name before a
byte is sent, so a corrupt or swapped file answers 500 and never leaves the
@ -550,7 +550,7 @@ host store may reuse its buffer). `onServerFault` reports every 500 (`hash-misma
```ts
import { readFileSync } from "node:fs";
import { createServer } from "node:https";
import { createContentChunkHandler, createDirectoryChunkStore, toNodeListener } from "cultmesh-ts/content";
import { createContentChunkHandler, createDirectoryChunkStore, toNodeListener } from "@gamecult/cultmesh-ts/content";
const handler = createContentChunkHandler(createDirectoryChunkStore("/srv/content"), {
onServerFault: fault => console.error("chunk fault", fault.kind, fault.chunkHash),

View file

@ -1,5 +1,5 @@
{
"name": "cultmesh-ts",
"name": "@gamecult/cultmesh-ts",
"version": "0.2.0",
"description": "CultMesh local typed state and mesh catalog primitives for TypeScript runtimes.",
"main": "dist/index.js",
@ -49,7 +49,7 @@
},
"dependencies": {
"@gamecult/cultcache-ts": "^0.14.0",
"cultnet-ts": "^0.3.0",
"@gamecult/cultnet-ts": "^0.3.0",
"koffi": "3.3.0"
},
"devDependencies": {

View file

@ -2,7 +2,7 @@ import { constants } from "node:fs";
import { open } from "node:fs/promises";
import type { IncomingMessage, ServerResponse } from "node:http";
import { join } from "node:path";
import { DEFAULT_CHUNK_SIZE_BYTES, EMPTY_SHA256, chunkRecordKey, isNullOrWhiteSpaceCSharp, parseSha256Hex, sha256Hex } from "cultnet-ts/content";
import { DEFAULT_CHUNK_SIZE_BYTES, EMPTY_SHA256, chunkRecordKey, isNullOrWhiteSpaceCSharp, parseSha256Hex, sha256Hex } from "@gamecult/cultnet-ts/content";
/** What the provider reads of a request. The path and the headers are never read: the route is the query alone. */
export interface ContentChunkRequest { method: string; url: string; }

View file

@ -1,5 +1,5 @@
import { createSocket, type Socket } from "node:dgram";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
CultCache,
@ -40,7 +40,7 @@ import {
type CultNetSchemaCatalogOptions,
type CultNetSnapshotResponseRawMessage,
type CultNetWireContract,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import {
CultMeshQuicRealtimeConnector,

View file

@ -1,7 +1,7 @@
import { randomUUID } from "node:crypto";
import { createSocket, type Socket } from "node:dgram";
import { documentPayloadFromBytes } from "@gamecult/cultcache-ts";
import { encodeWireValue } from "cultnet-ts/wire";
import { encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
CultNetPeer,
CultNetRudpSocketTransportConnection,
@ -9,7 +9,7 @@ import {
type CultNetOperationRequestMessage,
type CultNetOperationResponseMessage,
type CultNetRawDocumentRecord,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import type {
CultMeshProviderCommand,

View file

@ -1,11 +1,11 @@
import { encodeDocumentPayload } from "@gamecult/cultcache-ts";
import { encodeWireValue } from "cultnet-ts/wire";
import { encodeWireValue } from "@gamecult/cultnet-ts/wire";
import type {
CultNetDocumentPutRawMessage,
CultNetOperationRequestMessage,
CultNetOperationResponseMessage,
CultNetRawDocumentRecord,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import type { CultMeshRudpServerSession } from "./index";
import {

View file

@ -1,6 +1,6 @@
import { decodeDocumentPayload, defineDocumentType } from "@gamecult/cultcache-ts";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "cultnet-ts";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "@gamecult/cultnet-ts";
export const CULTMESH_PROVIDER_SESSION_SERVICE_ID = "gamecult.mesh.provider_session";

View file

@ -21,8 +21,8 @@ import {
trimCSharp,
verifyAuthorityRoute,
type CultMeshAuthorityTrustPolicy,
} from "cultnet-ts";
import type { CultMeshVerseDescriptorMessage } from "cultnet-ts/contracts";
} from "@gamecult/cultnet-ts";
import type { CultMeshVerseDescriptorMessage } from "@gamecult/cultnet-ts/contracts";
import {
decodeRealtimeFrame,

View file

@ -16,7 +16,7 @@
// agree with itself.
import { documentPayloadBytes, documentPayloadFromBytes, type DocumentPayload } from "@gamecult/cultcache-ts";
import { isNullOrWhiteSpaceCSharp } from "cultnet-ts";
import { isNullOrWhiteSpaceCSharp } from "@gamecult/cultnet-ts";
/** `CultMeshRealtimeWireProtocol.ApplicationProtocolName`: the QUIC ALPN. */
export const CULTMESH_REALTIME_ALPN = "cultmesh-state-v1";
@ -70,7 +70,7 @@ const INT64_MAX = 2n ** 63n - 1n;
* `CultMeshRealtimeFrame.Validate`. The C# reading of "non-empty" is
* `string.IsNullOrWhiteSpace`, whose whitespace set is not
* `String.prototype.trim`'s, so this borrows the one TypeScript implementation
* of that set from `cultnet-ts` rather than keeping a second reading here.
* of that set from `@gamecult/cultnet-ts` rather than keeping a second reading here.
*
* The range check has no C# counterpart because `long` gives it for free; a
* `bigint` does not, and a `DataView` would otherwise throw a bare `RangeError`

View file

@ -19,7 +19,7 @@ import {
CultNetDocumentRegistry,
defineCultNetDocumentBinding,
type CultNetPeer,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import { CultMesh, cultMeshDocumentFromSingleFile, type CultMeshRudpDocumentPut } from "../src/index";
// A document written in the reference's positional shape, and a plain map document beside it. Every mesh reader of a

View file

@ -8,7 +8,7 @@ import { join } from "node:path";
import test, { mock } from "node:test";
import { execFileSync } from "node:child_process";
import { EMPTY_SHA256, chunkRecordKey, packContent, sha256Hex } from "cultnet-ts/content";
import { EMPTY_SHA256, chunkRecordKey, packContent, sha256Hex } from "@gamecult/cultnet-ts/content";
import { closeSync, constants as fsConstants, openSync, symlinkSync } from "node:fs";
import {

View file

@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import assert from "node:assert/strict";
import { decodeWireValue, encodeWireValue } from "cultnet-ts/wire";
import { decodeWireValue, encodeWireValue } from "@gamecult/cultnet-ts/wire";
import { z } from "zod";
import { SchemaConflictError, defineDocumentType, documentPayloadBytes, documentPayloadFromBytes } from "@gamecult/cultcache-ts";
import {
@ -19,7 +19,7 @@ import {
type CultNetRudpPacket,
type CultNetMessage,
type CultNetSnapshotResponseRawMessage,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import { CultMesh, CultMeshRudpUnservableDocumentError } from "../src/index";
// A record's payload as the bytes a peer would put on the wire.

View file

@ -18,8 +18,8 @@
// harness can dial a peer late and exercise Cut C's retained-state seed
// instead of the provider always tearing down the moment it is done sending.
//
// Neither mode imports `cultmesh-ts` itself (the harness drives processes,
// and the dependency direction is `cultmesh-ts` -> `cultnet-ts`, never the
// Neither mode imports `@gamecult/cultmesh-ts` itself (the harness drives processes,
// and the dependency direction is `@gamecult/cultmesh-ts` -> `@gamecult/cultnet-ts`, never the
// reverse); this script reaches the provider/connector through its own
// package's `src/`.

View file

@ -8,8 +8,8 @@ import {
documentPayloadFromBytes,
type DocumentPayload,
} from "@gamecult/cultcache-ts";
import { decodeWireValue } from "cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "cultnet-ts";
import { decodeWireValue } from "@gamecult/cultnet-ts/wire";
import type { CultNetRawDocumentRecord } from "@gamecult/cultnet-ts";
import type { CultMeshRudpDocumentPut } from "../src/index";
import { decodeRealtimeFrame, encodeRealtimeFrame, type CultMeshRealtimeFrame } from "../src/realtime-wire";

View file

@ -2,7 +2,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import { createSocket } from "node:dgram";
import { documentPayloadBytes } from "@gamecult/cultcache-ts";
import { decodeWireValue } from "cultnet-ts/wire";
import { decodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
CultNetDocumentRegistry,
CultNetRudpSession,
@ -10,7 +10,7 @@ import {
encodeRudpPacket,
type CultNetOperationRequestMessage,
type CultNetRudpPacket,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import {
CultMesh,

View file

@ -2,7 +2,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import { documentPayloadBytes, documentPayloadFromBytes, encodeDocumentPayload, type DocumentPayload } from "@gamecult/cultcache-ts";
import { encodeWireValue } from "cultnet-ts/wire";
import { encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
cultMeshProviderSessionSchemas,

View file

@ -21,8 +21,8 @@ import {
type CultMeshAuthorityRouteView,
type CultMeshAuthorityTrustPolicy,
type CultMeshP256PublicKey,
} from "cultnet-ts";
import type { CultMeshAuthorityRouteMessage, CultMeshVerseDescriptorMessage } from "cultnet-ts/contracts";
} from "@gamecult/cultnet-ts";
import type { CultMeshAuthorityRouteMessage, CultMeshVerseDescriptorMessage } from "@gamecult/cultnet-ts/contracts";
import {
CultMeshQuicNativeRuntime,

View file

@ -14,8 +14,8 @@
import assert from "node:assert/strict";
import test from "node:test";
import type { CultMeshAuthorityTrustPolicy } from "cultnet-ts";
import type { CultMeshAuthorityRouteMessage, CultMeshVerseDescriptorMessage } from "cultnet-ts/contracts";
import type { CultMeshAuthorityTrustPolicy } from "@gamecult/cultnet-ts";
import type { CultMeshAuthorityRouteMessage, CultMeshVerseDescriptorMessage } from "@gamecult/cultnet-ts/contracts";
import {
CultMeshQuicRealtimeSessionManager,

View file

@ -6,14 +6,14 @@ import assert from "node:assert/strict";
import { createSocket, type RemoteInfo, type Socket } from "node:dgram";
import test from "node:test";
import { documentPayloadFromBytes } from "@gamecult/cultcache-ts";
import { encodeWireValue } from "cultnet-ts/wire";
import { encodeWireValue } from "@gamecult/cultnet-ts/wire";
import {
CultNetDocumentRegistry,
CultNetRudpSession,
decodeRudpPacket,
encodeRudpPacket,
type CultNetPeer,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
import { CultMesh } from "../src/index";
async function waitFor(predicate: () => boolean, description: string): Promise<void> {

View file

@ -8,7 +8,7 @@ wire contracts that both a node and a bare peer have to agree on.
It depends on `cultcache-py` and `msgpack`; AES-GCM helpers on `CultNetSecret`
light up with the optional `crypto` extra. `cultmesh-py` builds on it. The
layering mirrors `cultnet-rs` and `cultnet-ts`.
layering mirrors `cultnet-rs` and `@gamecult/cultnet-ts`.
## Example

View file

@ -1,6 +1,6 @@
# CultNet RS
`cultnet-rs` is the Rust sibling of `cultnet-ts`: typed MessagePack messages,
`cultnet-rs` is the Rust sibling of `@gamecult/cultnet-ts`: typed MessagePack messages,
4-byte length-prefixed direct-pipe framing, CultLib-shaped security helpers, and
CultCache replication without making callers paw through raw envelopes like we
lost a bet.
@ -100,7 +100,7 @@ game-session authority remain separate responsibilities.
## Local Fast Lane
`cultnet-rs` now mirrors the raw replication seam from `cultnet-ts`:
`cultnet-rs` now mirrors the raw replication seam from `@gamecult/cultnet-ts`:
- `cultnet.document_put_raw.v0`
- `cultnet.snapshot_response_raw.v0`

View file

@ -1,6 +1,6 @@
# Changelog
All notable changes to `cultnet-ts` are recorded here, following
All notable changes to `@gamecult/cultnet-ts` are recorded here, following
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## Unreleased
@ -13,6 +13,7 @@ All notable changes to `cultnet-ts` are recorded here, following
### Breaking
- The package is renamed `cultnet-ts` to `@gamecult/cultnet-ts` (the repo already scopes `@gamecult/cultcache-ts`), so the unscoped name cannot be claimed by someone else. Import from `@gamecult/cultnet-ts` and its subpaths; the API is unchanged.
- `CultNetErrorMessage` carries `routingHint`, `code` and `details` (null when absent), as the reference writes them and `cultnet.error.schema.json` requires; the interface previously dropped them. New `CultNetErrorCode`, `CultNetErrorDetails` and `CultNetShardRoutingHint` types.
- `CultMeshCdnChunkRef` carries `id` (slot 4), the reference's element id. `encodeManifest` writes five-slot chunk references and `packContent` sets the decimal offset as the id, as the reference does; a manifest written before ids reads `""` and is rewritten with the slot. Content vectors regenerated from the reference.

View file

@ -1,6 +1,6 @@
# CultNet TS
`cultnet-ts` is the TypeScript port of the useful shape of GameCult's
`@gamecult/cultnet-ts` is the TypeScript port of the useful shape of GameCult's
networking layer: typed MessagePack messages, direct-pipe framing, CultLib-style
security/session helpers, and state replication that hands typed values into a
`CultCache` instead of making consumer code paw raw database envelopes.
@ -42,7 +42,7 @@ We want:
So the split is:
- `cultcache-ts`: typed local persistence
- `cultnet-ts`: typed transport, auth, and state replication
- `@gamecult/cultnet-ts`: typed transport, auth, and state replication
Same family. Different organs. Nobody needs a second spine growing out of the
transport layer.
@ -80,7 +80,7 @@ CultNet without inventing a second ontology first.
## Generated Swarm Contracts
`cultnet-ts` can consume canonical JSON Schema files and generate:
`@gamecult/cultnet-ts` can consume canonical JSON Schema files and generate:
- TypeScript contract classes/interfaces
- runtime schema verifiers
@ -111,7 +111,7 @@ which exports:
## Wire Contracts
`cultnet-ts` does not sniff random payloads and pretend that counts as rigor.
`@gamecult/cultnet-ts` does not sniff random payloads and pretend that counts as rigor.
You choose the contract explicitly.
Current wire-contract options:
@ -129,7 +129,7 @@ away from the C# lineage.
## Content
`cultnet-ts/content` owns the TypeScript shapes of the CultMesh CDN
`@gamecult/cultnet-ts/content` owns the TypeScript shapes of the CultMesh CDN
(`src/content.ts`, the twin of `CultMeshCdn.cs`): the artifact manifest and chunk
reference, their MessagePack form, hash normalization, both record-key grammars,
SHA-256 hashing through WebCrypto, and `packContent`. It is browser-safe: it
@ -160,7 +160,7 @@ imports no `node:` module, so a browser bundle can take it.
## Local Fast Lane
For direct-pipe neighbors that already share the same CultCache formatter
contract, `cultnet-ts` now has a raw replication lane:
contract, `@gamecult/cultnet-ts` now has a raw replication lane:
- `cultnet.document_put_raw.v0`
- `cultnet.snapshot_response_raw.v0`
@ -185,7 +185,7 @@ cuts out the dumbest part of the trip.
Apps sharing a CultNet space should be able to ask each other what they can
exchange without resorting to folklore.
`cultnet-ts` now includes:
`@gamecult/cultnet-ts` now includes:
- `cultnet.schema_catalog_request.v0`
- `cultnet.schema_catalog_response.v0`
@ -232,7 +232,7 @@ import {
createTcpFramedCultNetPeer,
defineCultNetDocumentBinding,
ghostlightAgentStateGeneratedContract,
} from "cultnet-ts";
} from "@gamecult/cultnet-ts";
const ghostlightStateDefinition = defineDocumentType({
type: "ghostlight.agent-state",
@ -270,7 +270,7 @@ That means a TypeScript runtime can follow the same cryptographic and session
story as the C# version instead of inventing one more house religion around
"trust me, this pipe is probably fine."
For the legacy `gamecult.networking.v0` contract, `cultnet-ts` normalizes the
For the legacy `gamecult.networking.v0` contract, `@gamecult/cultnet-ts` normalizes the
encrypted binary fields as base64url strings in the ergonomic TS surface and
maps them back to the exact C# union payload shape at the wire boundary.

View file

@ -1,5 +1,5 @@
{
"name": "cultnet-ts",
"name": "@gamecult/cultnet-ts",
"version": "0.3.0",
"description": "CultNet-style typed direct-pipe messaging and state replication for TypeScript runtimes.",
"main": "dist/index.js",

View file

@ -39,8 +39,8 @@ export interface CultMeshAuthorityRouteCertificate {
/**
* Maps an advertised route's wire-shaped certificate to the view
* `verifyAuthorityRoute` and the transports consume. Shared by the QUIC
* realtime session manager (`cultmesh-ts`) and the browser WebSocket client
* (`cultmesh-browser`), which advertise the same `CultMeshAuthorityRouteMessage`
* realtime session manager (`@gamecult/cultmesh-ts`) and the browser WebSocket client
* (`@gamecult/cultmesh-browser`), which advertise the same `CultMeshAuthorityRouteMessage`
* certificate shape and were computing this mapping independently.
*/
export function routeCertificateView(

View file

@ -6,7 +6,7 @@ import test from "node:test";
// The TypeScript CultNet first-call snippet that docs/start/README.md shows: define a typed document, send it
// over a loopback TCP pipe as a cultnet.document_put_raw.v0 message, receive it into a second cache. The docs show
// the region between the `snippet` markers verbatim, except that the package import reads
// "cultnet-ts" where this file reads "../src"; the docs test below fails when they differ.
// "@gamecult/cultnet-ts" where this file reads "../src"; the docs test below fails when they differ.
// snippet:begin
import { createServer, connect } from "node:net";
import { z } from "zod";
@ -72,7 +72,7 @@ test("docs show the tested snippet", async () => {
const snippet = source
.split("// snippet:begin\n")[1]
.split("// snippet:end\n")[0]
.replace('"../src"', '"cultnet-ts"');
.replace('"../src"', '"@gamecult/cultnet-ts"');
const docs = (await readFile(join(__dirname, "..", "..", "..", "..", "docs", "start", "README.md"), "utf8")).replace(/\r\n/g, "\n");
assert.ok(docs.includes("```ts\n" + snippet + "```"), "docs/start/README.md no longer shows test/first-call.test.ts");
});

View file

@ -461,13 +461,13 @@ function loadUnderDgramStub(modulePath: string): string {
}
test("the idunn-runtime subpath loads the signer and loader without loading dgram; the package root does load it", () => {
const subpath = require.resolve("cultnet-ts/idunn-runtime");
const subpath = require.resolve("@gamecult/cultnet-ts/idunn-runtime");
assert.equal(loadUnderDgramStub(subpath), "loaded");
assert.equal(loadUnderDgramStub(require.resolve("cultnet-ts")), "failed: dgram was loaded");
assert.equal(loadUnderDgramStub(require.resolve("@gamecult/cultnet-ts")), "failed: dgram was loaded");
const exported = require(subpath) as Record<string, unknown>;
assert.equal(typeof exported.createIdunnRuntimeSigner, "function");
assert.equal(typeof exported.loadIdunnRuntimeAuthorityFromEnvironment, "function");
assert.equal(exported.createIdunnRuntimePresencePublisher, undefined);
assert.equal(exported.signIdunnRuntimePresence, undefined);
assert.equal((require("cultnet-ts") as Record<string, unknown>).signIdunnRuntimePresence, undefined);
assert.equal((require("@gamecult/cultnet-ts") as Record<string, unknown>).signIdunnRuntimePresence, undefined);
});

View file

@ -4,7 +4,7 @@ This is the real-process continuation of `samples/eve-two-runtime`:
- one C# provider owns a durable `.cc` counter, the Eve surface, command
idempotency, and canonical receipts;
- one real Chromium page leases both documents through `cultmesh-browser` and
- one real Chromium page leases both documents through `@gamecult/cultmesh-browser` and
lowers the provider-owned Eve surface;
- one retained C# headless client discovers the provider through the same Odin
identity, observes the typed counter and canonical receipt ids, survives the

View file

@ -5,7 +5,7 @@ import {
CultMeshBrowserOdinRendezvous,
decodeCultNetOperationPayload,
decodeCultNetPayload,
} from "cultmesh-browser";
} from "@gamecult/cultmesh-browser";
import { defineDocumentType } from "@gamecult/cultcache-ts/document";
// A payload is read through the definition that says how its bytes are laid out. These are plain-map definitions: the

View file

@ -5,7 +5,7 @@ import { join } from "node:path";
import { JSDOM } from "jsdom";
import { z } from "zod";
import { defineDocumentType } from "@gamecult/cultcache-ts";
import { CultMesh } from "cultmesh-ts";
import { CultMesh } from "@gamecult/cultmesh-ts";
import {
parseEveCommandReceipt,
parseEveSurfaceDocument,

View file

@ -11,7 +11,7 @@ const npmCli = process.env.npm_execpath ??
// moves when a package is scoped: `8cb3b72` renamed `cultcache-ts` to
// `@gamecult/cultcache-ts` and left this entry behind, which broke `build:ts` and
// `test:ts` for every workspace from that commit on.
const workspaces = ["@gamecult/cultcache-ts", "cultnet-ts", "cultmesh-ts", "cultmesh-browser"];
const workspaces = ["@gamecult/cultcache-ts", "@gamecult/cultnet-ts", "@gamecult/cultmesh-ts", "@gamecult/cultmesh-browser"];
const childEnv = process.platform === "win32"
? {
...process.env,

View file

@ -79,8 +79,8 @@ try {
const assert = require("node:assert/strict");
const cache = require("@gamecult/cultcache-ts");
const documentEntry = require("@gamecult/cultcache-ts/document");
const net = require("cultnet-ts");
const mesh = require("cultmesh-ts");
const net = require("@gamecult/cultnet-ts");
const mesh = require("@gamecult/cultmesh-ts");
assert.equal(typeof cache.defineDocumentType, "function");
// The document owner is also an entry of its own, which the browser client imports without the Node-only cache.
assert.equal(documentEntry.decodeDocumentPayload, cache.decodeDocumentPayload);
@ -147,8 +147,8 @@ assert.deepEqual(mesh.CultMesh.vec2(3, 4), { x: 3, y: 4 });
writeFileSync(join(consumerRoot, "browser-runtime-smoke.mjs"), `
import assert from "node:assert/strict";
import { CultMeshBrowserClient, decodeCultNetPayload } from "cultmesh-browser";
import { parseCultNetMessage } from "cultnet-ts/contracts";
import { CultMeshBrowserClient, decodeCultNetPayload } from "@gamecult/cultmesh-browser";
import { parseCultNetMessage } from "@gamecult/cultnet-ts/contracts";
assert.equal(typeof CultMeshBrowserClient.connect, "function");
assert.equal(typeof decodeCultNetPayload, "function");
assert.equal(typeof parseCultNetMessage, "function");
@ -159,14 +159,14 @@ assert.equal("putDocument" in CultMeshBrowserClient.prototype, false);
writeFileSync(join(consumerRoot, "types-smoke.ts"), `
import { defineDocumentType } from "@gamecult/cultcache-ts";
import { decodeDocumentPayload, type DocumentPayload } from "@gamecult/cultcache-ts/document";
import { CultNetPeer } from "cultnet-ts";
import { CultNetPeer } from "@gamecult/cultnet-ts";
import {
CultMesh,
CultMeshProviderSession,
type CultMeshProviderIdentity,
type CultMeshProviderTransport,
type CultMeshVec2,
} from "cultmesh-ts";
} from "@gamecult/cultmesh-ts";
const point: CultMeshVec2 = CultMesh.vec2(3, 4);
const defineDocument: typeof defineDocumentType = defineDocumentType;
@ -209,7 +209,7 @@ import {
CultMeshBrowserClient,
type CultMeshBrowserIdentity,
type CultMeshRawDocumentLeaseOptions,
} from "cultmesh-browser";
} from "@gamecult/cultmesh-browser";
const identity: CultMeshBrowserIdentity = {
verseId: "smoke",

View file

@ -46,8 +46,8 @@ try {
const moduleMap = {
"@gamecult/eve-contracts": join(eveRoot, "packages", "eve-contracts", "src", "index.ts"),
"@gamecult/eve-browser-lowering": join(eveRoot, "packages", "eve-browser-lowering", "src", "index.ts"),
"cultmesh-browser": join(repoRoot, "packages", "cultmesh-browser", "src", "index.ts"),
"cultnet-ts/contracts": join(repoRoot, "packages", "cultnet-ts", "src", "contracts.ts"),
"@gamecult/cultmesh-browser": join(repoRoot, "packages", "cultmesh-browser", "src", "index.ts"),
"@gamecult/cultnet-ts/contracts": join(repoRoot, "packages", "cultnet-ts", "src", "contracts.ts"),
};
const sampleTsconfig = join(workRoot, "tsconfig.json");
await writeFile(sampleTsconfig, JSON.stringify({

View file

@ -19,14 +19,14 @@ standalone.
The current substrate has a browser client boundary and local live proof, but
not a complete public Verse proof:
- `cultmesh-ts` is a Node package and imports `node:dgram` at its root;
- `cultnet-ts/contracts` is the browser-safe contract entrypoint; the package
- `@gamecult/cultmesh-ts` is a Node package and imports `node:dgram` at its root;
- `@gamecult/cultnet-ts/contracts` is the browser-safe contract entrypoint; the package
root still includes Node transport, filesystem, crypto, stream, and UDP
modules and must not enter browser bundles;
- C# and TypeScript now share `cultnet.database_subscribe.v0`,
`cultnet.database_unsubscribe.v0`, and `cultnet.database_change_raw.v0`;
`cultmesh-browser` consumes those messages as explicit document leases;
- `cultmesh-browser` owns stable identity, route refresh, resubscription, and
`@gamecult/cultmesh-browser` consumes those messages as explicit document leases;
- `@gamecult/cultmesh-browser` owns stable identity, route refresh, resubscription, and
typed operation correlation without exposing direct document writes;
- `CultMeshBrowserOdinRendezvous` resolves stable Verse/provider identity with
`cultmesh.verse_catalog_request.v0` and
@ -112,8 +112,8 @@ Shared paths:
Deletion line:
- the jsdom checkpoint remains labeled as a Node-hosted DOM proof;
- browser applications import `cultmesh-browser` and
`cultnet-ts/contracts`, never the Node-only package roots;
- browser applications import `@gamecult/cultmesh-browser` and
`@gamecult/cultnet-ts/contracts`, never the Node-only package roots;
- subscription messages remain one shared schema contract across the C# and
TypeScript catalogs;
- the real provider adapter must consume the canonical WebSocket message lane;

View file

@ -101,7 +101,7 @@ The real-process C# sample is then rebuilt from that same temporary NuGet feed.
The source project references in its `.csproj` are contributor convenience only
and are disabled by the checkpoint; they are not the consumer contract.
The first layer builds and packs `cultcache-ts`, `cultnet-ts`, `cultmesh-ts`,
The first layer builds and packs `cultcache-ts`, `@gamecult/cultnet-ts`, `@gamecult/cultmesh-ts`,
`@gamecult/eve-contracts`, and `@gamecult/eve-browser-lowering`, installs the
tarballs into an empty temporary consumer, and runs
`samples/eve-two-runtime/sample.mjs`. Eve's generated runtime validators reject

View file

@ -574,7 +574,7 @@ registration, renewal, stable publication order, reconnect and replay,
duplicate receipt reuse, explicit withdrawal, and the negative stop-during-
registration timeline, durable receipt replay, observer isolation, conflicting
command IDs, and exception-safe shutdown. The empty-consumer tarball smoke proves the session is
present in the installable `cultmesh-ts` package. A live Odin transport adapter
present in the installable `@gamecult/cultmesh-ts` package. A live Odin transport adapter
and the VoidBot migration remain required for the production proof.
## Phase 3: Authority as a Mandatory Boundary

View file

@ -431,7 +431,7 @@ opt-in. Text frames and messages over the configured size bound are closed
before schema dispatch.
The matching `CultNetWebSocketSchemaClient` is suitable for C# headless and
service consumers. Browser applications use `cultmesh-browser`, which owns
service consumers. Browser applications use `@gamecult/cultmesh-browser`, which owns
stable Verse/provider identity, document leases, resubscription, and operation
correlation above the raw WebSocket route.