npm: scope cultnet-ts, cultmesh-ts and cultmesh-browser under @gamecult
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 5m49s
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Successful in 36s

Unscoped npm names are unclaimed and open to dependency confusion; cultcache-ts was
already @gamecult/cultcache-ts. package.json names, intra-repo dependencies, package-lock,
import specifiers (src, tests, samples, docs/start, first-call docs test), the workspace
list in run-typescript-workspaces, the verify-eve module map and package READMEs now use
the scoped names. Directory names, workspace paths in CI and release tag prefixes are
unchanged. Historical cut docs and past CHANGELOG entries keep the old names.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Libby 2026-11-07 16:25:10 +00:00
commit 286a9af389
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.