From 61549aa90bf1937a2bd4fc7d9d190d74d366f69e Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 13 Sep 2026 18:03:29 +0200 Subject: [PATCH] Make cultnet-ts a self-contained package again; revert fa00f3f's rootDir widen REC-032 Soul finding: dist/contracts.js required "../../../contracts/cultnet/...", escaping the cultnet-ts package tree entirely. That breaks every installed consumer: the two-runtime sample script (packs and installs cultnet-ts into a clean consumer), test-typescript-package-closure.mjs, and Heimdall's pnpm copy of cultnet-ts (honours `files`, would 404 on its next vendor/CultLib bump). Only Stonks (file: symlink into the live checkout) happened to still work. Fix: keep CultLib/contracts/cultnet as the only tracked source, but have tools/generate-swarm-contracts.mjs wipe and re-copy it into a package-local, gitignored packages/cultnet-ts/contracts/cultnet/ every time it runs (build, typecheck, test, test:interop, prepack, the two-runtime script, and Heimdral's `npm run build` all already invoke it). src/contracts.ts and the swarm-contracts manifest now import that package-local copy ("../contracts/cultnet/...") instead of reaching three directories up. package.json gets back `files: ["dist", "contracts"]` and `prepack` so a packed tarball carries its own schema copy. Also revert fa00f3f's tsconfig.test.json rootDir widen ("." -> "../..") and the matching test-script/dist-test path changes and __dirname-relative recomputation in cultnet-interop.test.ts and runtime-presence-health.test.ts: that widening was working around dist-test not receiving a copy of the (now out-of-package) contracts, but nothing in this repo actually needs the wider rootDir once the schemas are package-local again, and the widened paths never matched what CI's interop lanes and package.json's own dist-test paths expect -- packages/cultnet-ts/dist-test/test/interop/cultnet-interop.test.js, not .../dist-test/packages/cultnet-ts/test/interop/.... PR #3 CI run 34766892424 on 7d3a3dc failed the first interop lane on exactly this missing-file defect. cultnet-interop.test.ts's interopSchemaPath already resolved correctly once cultNetTsRoot was reverted (it was never touched by fa00f3f) and continues to point at the canonical CultLib/contracts/cultnet/interop copy, which is fine since it is only read by the spawned non-TS peer processes, not required() by this package. The cultmesh-ts shard-catalog $id lookup fix from fa00f3f (cultnet.shard_catalog_request.v0 -> the CultLib contracts URL id) is unrelated to the rootDir defect and is left as is. REC-032, Soul redirect on branch claude/rec-032-cultnet-contracts. Co-Authored-By: Claude Sonnet 5 --- packages/cultnet-ts/.gitignore | 3 + packages/cultnet-ts/package.json | 8 ++- packages/cultnet-ts/src/contracts.ts | 66 +++++++++---------- .../generated/swarm-contracts.generated.ts | 2 +- .../test/interop/cultnet-interop.test.ts | 2 +- .../test/runtime-presence-health.test.ts | 2 +- .../tools/generate-swarm-contracts.mjs | 22 +++++-- .../tools/swarm-contracts.manifest.json | 2 +- packages/cultnet-ts/tsconfig.test.json | 2 +- 9 files changed, 64 insertions(+), 45 deletions(-) diff --git a/packages/cultnet-ts/.gitignore b/packages/cultnet-ts/.gitignore index fd3ec924..fd7098ff 100644 --- a/packages/cultnet-ts/.gitignore +++ b/packages/cultnet-ts/.gitignore @@ -2,3 +2,6 @@ node_modules/ dist/ dist-test/ *.tgz +# Mirrored from CultLib/contracts/cultnet by tools/generate-swarm-contracts.mjs +# (REC-032). Not tracked: CultLib/contracts/cultnet is the only source. +contracts/ diff --git a/packages/cultnet-ts/package.json b/packages/cultnet-ts/package.json index 0b101282..f94f0bfa 100644 --- a/packages/cultnet-ts/package.json +++ b/packages/cultnet-ts/package.json @@ -17,14 +17,16 @@ } }, "files": [ - "dist" + "dist", + "contracts" ], "scripts": { "contracts:generate": "node tools/generate-swarm-contracts.mjs", "build": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json", + "prepack": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json", "typecheck": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json --pretty false", - "test": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node ../../node_modules/typescript/bin/tsc -p tsconfig.test.json && node --test dist-test/packages/cultnet-ts/test/*.test.js", - "test:interop": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node ../../node_modules/typescript/bin/tsc -p tsconfig.test.json && node --test --test-force-exit dist-test/packages/cultnet-ts/test/interop/*.test.js" + "test": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node ../../node_modules/typescript/bin/tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js", + "test:interop": "node tools/generate-swarm-contracts.mjs && node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node ../../node_modules/typescript/bin/tsc -p tsconfig.test.json && node --test --test-force-exit dist-test/test/interop/*.test.js" }, "keywords": [ "messagepack", diff --git a/packages/cultnet-ts/src/contracts.ts b/packages/cultnet-ts/src/contracts.ts index d44f391f..d90af381 100644 --- a/packages/cultnet-ts/src/contracts.ts +++ b/packages/cultnet-ts/src/contracts.ts @@ -1,38 +1,38 @@ import Ajv2020, { type ValidateFunction } from "ajv/dist/2020"; -import documentRecordSchema from "../../../contracts/cultnet/cultnet.document-record.schema.json"; -import helloSchema from "../../../contracts/cultnet/cultnet.hello.schema.json"; -import loginSchema from "../../../contracts/cultnet/cultnet.login.schema.json"; -import registerSchema from "../../../contracts/cultnet/cultnet.register.schema.json"; -import verifySchema from "../../../contracts/cultnet/cultnet.verify.schema.json"; -import loginSuccessSchema from "../../../contracts/cultnet/cultnet.login-success.schema.json"; -import errorSchema from "../../../contracts/cultnet/cultnet.error.schema.json"; -import sampleChangeNameSchema from "../../../contracts/cultnet/cultnet.sample-change-name.schema.json"; -import sampleChatSchema from "../../../contracts/cultnet/cultnet.sample-chat.schema.json"; -import documentPutSchema from "../../../contracts/cultnet/cultnet.document-put.schema.json"; -import documentDeleteSchema from "../../../contracts/cultnet/cultnet.document-delete.schema.json"; -import rawDocumentRecordSchema from "../../../contracts/cultnet/cultnet.raw-document-record.schema.json"; -import documentPutRawSchema from "../../../contracts/cultnet/cultnet.document-put-raw.schema.json"; -import documentMutationContractSchema from "../../../contracts/cultnet/cultnet.document-mutation-contract.schema.json"; -import transportProfileSchema from "../../../contracts/cultnet/cultnet.transport-profile.schema.json"; -import snapshotRequestSchema from "../../../contracts/cultnet/cultnet.snapshot-request.schema.json"; -import snapshotResponseSchema from "../../../contracts/cultnet/cultnet.snapshot-response.schema.json"; -import snapshotResponseRawSchema from "../../../contracts/cultnet/cultnet.snapshot-response-raw.schema.json"; -import databaseSubscribeSchema from "../../../contracts/cultnet/cultnet.database-subscribe.schema.json"; -import databaseUnsubscribeSchema from "../../../contracts/cultnet/cultnet.database-unsubscribe.schema.json"; -import databaseChangeRawSchema from "../../../contracts/cultnet/cultnet.database-change-raw.schema.json"; -import schemaDescriptorSchema from "../../../contracts/cultnet/cultnet.schema-descriptor.schema.json"; -import schemaCatalogRequestSchema from "../../../contracts/cultnet/cultnet.schema-catalog-request.schema.json"; -import schemaCatalogResponseSchema from "../../../contracts/cultnet/cultnet.schema-catalog-response.schema.json"; -import shardCatalogRequestSchema from "../../../contracts/cultnet/cultnet.shard-catalog-request.schema.json"; -import shardCatalogResponseSchema from "../../../contracts/cultnet/cultnet.shard-catalog-response.schema.json"; -import operationRequestSchema from "../../../contracts/cultnet/cultnet.operation-request.schema.json"; -import operationResponseSchema from "../../../contracts/cultnet/cultnet.operation-response.schema.json"; -import verseCatalogRequestSchema from "../../../contracts/cultnet/cultmesh.verse-catalog-request.schema.json"; -import verseCatalogResponseSchema from "../../../contracts/cultnet/cultmesh.verse-catalog-response.schema.json"; -import sessionOpenSchema from "../../../contracts/cultnet/cultmesh.session-open.schema.json"; -import sessionAcceptedSchema from "../../../contracts/cultnet/cultmesh.session-accepted.schema.json"; -import ghostlightAgentStateSchema from "../../../contracts/cultnet/ghostlight.agent-state.schema.json"; +import documentRecordSchema from "../contracts/cultnet/cultnet.document-record.schema.json"; +import helloSchema from "../contracts/cultnet/cultnet.hello.schema.json"; +import loginSchema from "../contracts/cultnet/cultnet.login.schema.json"; +import registerSchema from "../contracts/cultnet/cultnet.register.schema.json"; +import verifySchema from "../contracts/cultnet/cultnet.verify.schema.json"; +import loginSuccessSchema from "../contracts/cultnet/cultnet.login-success.schema.json"; +import errorSchema from "../contracts/cultnet/cultnet.error.schema.json"; +import sampleChangeNameSchema from "../contracts/cultnet/cultnet.sample-change-name.schema.json"; +import sampleChatSchema from "../contracts/cultnet/cultnet.sample-chat.schema.json"; +import documentPutSchema from "../contracts/cultnet/cultnet.document-put.schema.json"; +import documentDeleteSchema from "../contracts/cultnet/cultnet.document-delete.schema.json"; +import rawDocumentRecordSchema from "../contracts/cultnet/cultnet.raw-document-record.schema.json"; +import documentPutRawSchema from "../contracts/cultnet/cultnet.document-put-raw.schema.json"; +import documentMutationContractSchema from "../contracts/cultnet/cultnet.document-mutation-contract.schema.json"; +import transportProfileSchema from "../contracts/cultnet/cultnet.transport-profile.schema.json"; +import snapshotRequestSchema from "../contracts/cultnet/cultnet.snapshot-request.schema.json"; +import snapshotResponseSchema from "../contracts/cultnet/cultnet.snapshot-response.schema.json"; +import snapshotResponseRawSchema from "../contracts/cultnet/cultnet.snapshot-response-raw.schema.json"; +import databaseSubscribeSchema from "../contracts/cultnet/cultnet.database-subscribe.schema.json"; +import databaseUnsubscribeSchema from "../contracts/cultnet/cultnet.database-unsubscribe.schema.json"; +import databaseChangeRawSchema from "../contracts/cultnet/cultnet.database-change-raw.schema.json"; +import schemaDescriptorSchema from "../contracts/cultnet/cultnet.schema-descriptor.schema.json"; +import schemaCatalogRequestSchema from "../contracts/cultnet/cultnet.schema-catalog-request.schema.json"; +import schemaCatalogResponseSchema from "../contracts/cultnet/cultnet.schema-catalog-response.schema.json"; +import shardCatalogRequestSchema from "../contracts/cultnet/cultnet.shard-catalog-request.schema.json"; +import shardCatalogResponseSchema from "../contracts/cultnet/cultnet.shard-catalog-response.schema.json"; +import operationRequestSchema from "../contracts/cultnet/cultnet.operation-request.schema.json"; +import operationResponseSchema from "../contracts/cultnet/cultnet.operation-response.schema.json"; +import verseCatalogRequestSchema from "../contracts/cultnet/cultmesh.verse-catalog-request.schema.json"; +import verseCatalogResponseSchema from "../contracts/cultnet/cultmesh.verse-catalog-response.schema.json"; +import sessionOpenSchema from "../contracts/cultnet/cultmesh.session-open.schema.json"; +import sessionAcceptedSchema from "../contracts/cultnet/cultmesh.session-accepted.schema.json"; +import ghostlightAgentStateSchema from "../contracts/cultnet/ghostlight.agent-state.schema.json"; export type CultNetWireContract = "cultnet.schema.v0" | "gamecult.networking.v0"; diff --git a/packages/cultnet-ts/src/generated/swarm-contracts.generated.ts b/packages/cultnet-ts/src/generated/swarm-contracts.generated.ts index 84bb09b0..f2dffce2 100644 --- a/packages/cultnet-ts/src/generated/swarm-contracts.generated.ts +++ b/packages/cultnet-ts/src/generated/swarm-contracts.generated.ts @@ -1,5 +1,5 @@ import { defineJsonSchemaContract } from "../json-schema-contract"; -import ghostlightAgentStateGeneratedContractSchema from "../../../../contracts/cultnet/ghostlight.agent-state.schema.json"; +import ghostlightAgentStateGeneratedContractSchema from "../../contracts/cultnet/ghostlight.agent-state.schema.json"; // Generated by tools/generate-swarm-contracts.mjs. Do not hand-edit this file unless you enjoy losing arguments with a machine. diff --git a/packages/cultnet-ts/test/interop/cultnet-interop.test.ts b/packages/cultnet-ts/test/interop/cultnet-interop.test.ts index 07fb268b..51bea1ff 100644 --- a/packages/cultnet-ts/test/interop/cultnet-interop.test.ts +++ b/packages/cultnet-ts/test/interop/cultnet-interop.test.ts @@ -21,7 +21,7 @@ const execFileAsync = promisify(execFile); const cargoCommand = process.env.CARGO ?? (process.platform === "win32" ? join(homedir(), ".cargo", "bin", "cargo.exe") : "cargo"); const dotnetCommand = process.env.DOTNET ?? (process.platform === "win32" ? join("C:", "Program Files", "dotnet", "dotnet.exe") : "dotnet"); const pythonCommand = process.env.PYTHON ?? resolvePythonCommand(); -const cultNetTsRoot = resolve(__dirname, "../../../../.."); +const cultNetTsRoot = resolve(__dirname, "../../.."); const cultLibRoot = findAncestor(cultNetTsRoot, "CultLib.sln") ?? resolve(cultNetTsRoot, "..", "CultLib"); const cultcachePyRoot = resolve(cultLibRoot, "packages", "cultcache-py"); const cultcachePySrc = ["cultcache-py", "cultnet-py", "cultmesh-py"] diff --git a/packages/cultnet-ts/test/runtime-presence-health.test.ts b/packages/cultnet-ts/test/runtime-presence-health.test.ts index 2ceedde3..fa21aeaa 100644 --- a/packages/cultnet-ts/test/runtime-presence-health.test.ts +++ b/packages/cultnet-ts/test/runtime-presence-health.test.ts @@ -26,7 +26,7 @@ import { // are generated output and must have exactly one home. const VECTORS = JSON.parse( readFileSync( - join(__dirname, "..", "..", "..", "..", "test", "fixtures", "runtime-presence-vectors.json"), + join(__dirname, "..", "..", "test", "fixtures", "runtime-presence-vectors.json"), "utf8", ), ) as { proof: string; signed: string }; diff --git a/packages/cultnet-ts/tools/generate-swarm-contracts.mjs b/packages/cultnet-ts/tools/generate-swarm-contracts.mjs index 53ad1e18..dd27ad7c 100644 --- a/packages/cultnet-ts/tools/generate-swarm-contracts.mjs +++ b/packages/cultnet-ts/tools/generate-swarm-contracts.mjs @@ -1,4 +1,4 @@ -import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -6,9 +6,23 @@ import { compile } from "json-schema-to-typescript"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const repoRoot = path.resolve(__dirname, ".."); -const manifestPath = path.join(repoRoot, "tools", "swarm-contracts.manifest.json"); -const outputPath = path.join(repoRoot, "src", "generated", "swarm-contracts.generated.ts"); +const packageRoot = path.resolve(__dirname, ".."); +const cultLibRoot = path.resolve(packageRoot, "..", ".."); +const manifestPath = path.join(packageRoot, "tools", "swarm-contracts.manifest.json"); +const outputPath = path.join(packageRoot, "src", "generated", "swarm-contracts.generated.ts"); + +// CultLib/contracts/cultnet is the single source of CultNet schema contracts +// (REC-032). cultnet-ts is a standalone npm package (published or vendored by +// Heimdall independent of the CultLib checkout), so it cannot import schemas +// that live outside its own package tree. Mirror the canonical contracts into +// a package-local, gitignored copy every time this generator runs, so `dist` +// and `contracts` both ship self-contained. Wipe first: a stale local file +// left behind after a schema is deleted upstream must not survive as drift. +const sourceContractsDir = path.join(cultLibRoot, "contracts", "cultnet"); +const localContractsDir = path.join(packageRoot, "contracts", "cultnet"); +await rm(localContractsDir, { recursive: true, force: true }); +await mkdir(path.dirname(localContractsDir), { recursive: true }); +await cp(sourceContractsDir, localContractsDir, { recursive: true }); /** * @typedef {{ diff --git a/packages/cultnet-ts/tools/swarm-contracts.manifest.json b/packages/cultnet-ts/tools/swarm-contracts.manifest.json index 49b5073d..f5750740 100644 --- a/packages/cultnet-ts/tools/swarm-contracts.manifest.json +++ b/packages/cultnet-ts/tools/swarm-contracts.manifest.json @@ -1,7 +1,7 @@ { "contracts": [ { - "schemaPath": "../../../contracts/cultnet/ghostlight.agent-state.schema.json", + "schemaPath": "../contracts/cultnet/ghostlight.agent-state.schema.json", "typeName": "GhostlightAgentStateShape", "contractExportName": "ghostlightAgentStateGeneratedContract", "validateExportName": "validateGhostlightAgentStateGenerated", diff --git a/packages/cultnet-ts/tsconfig.test.json b/packages/cultnet-ts/tsconfig.test.json index fc47617c..a73b52c0 100644 --- a/packages/cultnet-ts/tsconfig.test.json +++ b/packages/cultnet-ts/tsconfig.test.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "rootDir": "../..", + "rootDir": ".", "outDir": "dist-test", "declaration": false, "declarationMap": false