npm: scope cultnet-ts, cultmesh-ts, cultmesh-browser under @gamecult #28
55 changed files with 142 additions and 131 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
33
package-lock.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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`); };
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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/`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue