Make TypeScript packages release-packable

This commit is contained in:
Meta Vens 2026-07-13 23:47:38 +01:00
commit 6528f8ea2f
8 changed files with 298 additions and 130 deletions

View file

@ -1,24 +1,15 @@
{
"name": "cultnet-ts",
"version": "0.1.0",
"name": "cultlib",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cultnet-ts",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"ajv": "^8.17.1",
"cultcache-ts": "github:GameCult/cultcache-ts"
},
"devDependencies": {
"@types/node": "^24.0.0",
"json-schema-to-typescript": "^15.0.4",
"typescript": "^5.9.0",
"zod": "^3.25.76"
}
"name": "cultlib",
"workspaces": [
"packages/cultcache-ts",
"packages/cultnet-ts",
"packages/cultmesh-ts"
]
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "11.9.3",
@ -69,13 +60,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
"undici-types": "~7.18.0"
}
},
"node_modules/ajv": {
@ -102,13 +93,16 @@
"license": "Python-2.0"
},
"node_modules/cultcache-ts": {
"version": "0.1.0",
"resolved": "git+ssh://git@github.com/GameCult/cultcache-ts.git#6c2e1e3691146f99c44c098371cc8c787b4a8d0c",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"zod": "^3.25.76"
}
"resolved": "packages/cultcache-ts",
"link": true
},
"node_modules/cultmesh-ts": {
"resolved": "packages/cultmesh-ts",
"link": true
},
"node_modules/cultnet-ts": {
"resolved": "packages/cultnet-ts",
"link": true
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
@ -117,9 +111,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
"funding": [
{
"type": "github",
@ -174,10 +168,20 @@
}
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@ -234,9 +238,9 @@
}
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
@ -247,9 +251,9 @@
}
},
"node_modules/prettier": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"version": "3.9.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz",
"integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==",
"dev": true,
"license": "MIT",
"bin": {
@ -272,9 +276,9 @@
}
},
"node_modules/tinyglobby": {
"version": "0.2.16",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -303,9 +307,9 @@
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},
@ -317,6 +321,56 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"packages/cultcache-ts": {
"version": "0.13.5",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/node": "^24.0.0",
"typescript": "^5.9.0"
},
"engines": {
"node": ">=18"
}
},
"packages/cultmesh-ts": {
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.2",
"cultcache-ts": "^0.13.5",
"cultnet-ts": "^0.1.0"
},
"devDependencies": {
"@types/node": "^24.0.0",
"typescript": "^5.9.0",
"zod": "^3.25.76"
},
"engines": {
"node": ">=18"
}
},
"packages/cultnet-ts": {
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"ajv": "^8.17.1",
"cultcache-ts": "^0.13.5"
},
"devDependencies": {
"@types/node": "^24.0.0",
"json-schema-to-typescript": "^15.0.4",
"typescript": "^5.9.0",
"zod": "^3.25.76"
},
"engines": {
"node": ">=18"
}
}
}
}

View file

@ -7,7 +7,8 @@
"packages/cultmesh-ts"
],
"scripts": {
"build:ts": "npm --workspaces run build",
"test:ts": "npm --workspaces run test"
"build:ts": "node scripts/run-typescript-workspaces.mjs build",
"test:ts": "node scripts/run-typescript-workspaces.mjs test",
"test:pack:ts": "node scripts/test-typescript-package-closure.mjs"
}
}

View file

@ -1,70 +0,0 @@
{
"name": "cultcache-ts",
"version": "0.13.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cultcache-ts",
"version": "0.13.5",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/node": "^24.0.0",
"typescript": "^5.9.0"
}
},
"node_modules/@msgpack/msgpack": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz",
"integrity": "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==",
"license": "ISC",
"engines": {
"node": ">= 18"
}
},
"node_modules/@types/node": {
"version": "24.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View file

@ -20,9 +20,10 @@
"dist"
],
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --pretty false",
"test": "npm run build && tsc -p tsconfig.test.json && node --test dist-test/test/cult-cache.test.js"
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
"prepack": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --pretty false",
"test": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node ../../node_modules/typescript/bin/tsc -p tsconfig.test.json && node --test dist-test/test/cult-cache.test.js"
},
"keywords": [
"messagepack",
@ -34,6 +35,18 @@
],
"author": "metacratic",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/GameCult/CultLib.git",
"directory": "packages/cultcache-ts"
},
"bugs": {
"url": "https://github.com/GameCult/CultLib/issues"
},
"homepage": "https://github.com/GameCult/CultLib/tree/main/packages/cultcache-ts#readme",
"engines": {
"node": ">=18"
},
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"zod": "^3.25.76"

View file

@ -4,13 +4,21 @@
"description": "CultMesh local typed state and mesh catalog primitives for TypeScript runtimes.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --pretty false",
"test": "npm run build && tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js"
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
"prepack": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --pretty false",
"test": "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"
},
"keywords": [
"cultmesh",
@ -21,10 +29,22 @@
],
"author": "metacratic",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/GameCult/CultLib.git",
"directory": "packages/cultmesh-ts"
},
"bugs": {
"url": "https://github.com/GameCult/CultLib/issues"
},
"homepage": "https://github.com/GameCult/CultLib/tree/main/packages/cultmesh-ts#readme",
"engines": {
"node": ">=18"
},
"dependencies": {
"@msgpack/msgpack": "^3.1.2",
"cultcache-ts": "file:../cultcache-ts",
"cultnet-ts": "file:../cultnet-ts"
"cultcache-ts": "^0.13.5",
"cultnet-ts": "^0.1.0"
},
"devDependencies": {
"@types/node": "^24.0.0",

View file

@ -4,16 +4,24 @@
"description": "CultNet-style typed direct-pipe messaging and state replication for TypeScript runtimes.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
"dist",
"contracts"
],
"scripts": {
"contracts:generate": "node tools/generate-swarm-contracts.mjs",
"build": "npm run contracts:generate && tsc -p tsconfig.json",
"typecheck": "npm run contracts:generate && tsc -p tsconfig.json --pretty false",
"test": "npm run build && tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js",
"test:interop": "npm run build && tsc -p tsconfig.test.json && node --test dist-test/test/interop/*.test.js"
"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/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 dist-test/test/interop/*.test.js"
},
"keywords": [
"messagepack",
@ -25,10 +33,22 @@
],
"author": "metacratic",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/GameCult/CultLib.git",
"directory": "packages/cultnet-ts"
},
"bugs": {
"url": "https://github.com/GameCult/CultLib/issues"
},
"homepage": "https://github.com/GameCult/CultLib/tree/main/packages/cultnet-ts#readme",
"engines": {
"node": ">=18"
},
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"ajv": "^8.17.1",
"cultcache-ts": "file:../cultcache-ts"
"cultcache-ts": "^0.13.5"
},
"devDependencies": {
"@types/node": "^24.0.0",

View file

@ -0,0 +1,30 @@
import { execFileSync } from "node:child_process";
import { dirname, join } from "node:path";
const command = process.argv[2];
if (!command) throw new Error("Usage: node scripts/run-typescript-workspaces.mjs <script>");
const npmCli = process.env.npm_execpath ??
join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
const workspaces = ["cultcache-ts", "cultnet-ts", "cultmesh-ts"];
const childEnv = process.platform === "win32"
? {
...process.env,
PATH: [
dirname(process.execPath),
`${process.env.SystemRoot}\\System32`,
process.env.SystemRoot,
`${process.env.USERPROFILE}\\AppData\\Local\\Microsoft\\WindowsApps`,
`${process.env.USERPROFILE}\\.cargo\\bin`,
"C:\\Program Files\\dotnet",
"C:\\Program Files\\Git\\cmd",
].join(";"),
}
: process.env;
for (const workspace of workspaces) {
execFileSync(process.execPath, [npmCli, "--workspace", workspace, "run", command], {
env: childEnv,
stdio: "inherit",
});
}

View file

@ -0,0 +1,100 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const workspacePackages = ["cultcache-ts", "cultnet-ts", "cultmesh-ts"];
const npmCli = process.env.npm_execpath ??
join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
const childEnv = process.platform === "win32"
? {
...process.env,
PATH: `${dirname(process.execPath)};${process.env.SystemRoot}\\System32;${process.env.SystemRoot}`,
}
: process.env;
const smokeRoot = mkdtempSync(join(tmpdir(), "cultlib-ts-package-smoke-"));
const tarballRoot = join(smokeRoot, "tarballs");
const consumerRoot = join(smokeRoot, "consumer");
mkdirSync(tarballRoot);
mkdirSync(consumerRoot);
function npm(...args) {
return execFileSync(process.execPath, [npmCli, ...args], {
cwd: repoRoot,
encoding: "utf8",
env: childEnv,
stdio: ["ignore", "pipe", "inherit"],
});
}
try {
const tarballs = [];
for (const packageName of workspacePackages) {
const packageRoot = join(repoRoot, "packages", packageName);
const packed = JSON.parse(npm("pack", packageRoot, "--json", "--pack-destination", tarballRoot));
assert.equal(packed.length, 1, `${packageName} must produce exactly one tarball`);
const files = packed[0].files.map(file => file.path);
assert.ok(files.includes("dist/index.js"), `${packageName} tarball is missing dist/index.js`);
assert.ok(files.includes("dist/index.d.ts"), `${packageName} tarball is missing dist/index.d.ts`);
tarballs.push(join(tarballRoot, packed[0].filename));
}
writeFileSync(join(consumerRoot, "package.json"), JSON.stringify({
name: "cultlib-ts-package-smoke",
version: "1.0.0",
private: true,
}, null, 2));
execFileSync(process.execPath, [npmCli,
"install",
"--ignore-scripts",
"--no-audit",
"--no-fund",
...tarballs,
], { cwd: consumerRoot, env: childEnv, stdio: "inherit" });
writeFileSync(join(consumerRoot, "runtime-smoke.cjs"), `
const assert = require("node:assert/strict");
const cache = require("cultcache-ts");
const net = require("cultnet-ts");
const mesh = require("cultmesh-ts");
assert.equal(typeof cache.defineDocumentType, "function");
assert.equal(typeof net.CultNetPeer, "function");
assert.equal(typeof mesh.CultMesh, "function");
assert.deepEqual(mesh.CultMesh.vec2(3, 4), { x: 3, y: 4 });
`);
execFileSync(process.execPath, ["runtime-smoke.cjs"], { cwd: consumerRoot, stdio: "inherit" });
writeFileSync(join(consumerRoot, "types-smoke.ts"), `
import { defineDocumentType } from "cultcache-ts";
import { CultNetPeer } from "cultnet-ts";
import { CultMesh, type CultMeshVec2 } from "cultmesh-ts";
const point: CultMeshVec2 = CultMesh.vec2(3, 4);
const defineDocument: typeof defineDocumentType = defineDocumentType;
const peerType: typeof CultNetPeer = CultNetPeer;
void point;
void defineDocument;
void peerType;
`);
const tsc = join(repoRoot, "node_modules", "typescript", "bin", "tsc");
assert.ok(readFileSync(tsc, "utf8").length > 0, "workspace TypeScript compiler is unavailable");
execFileSync(process.execPath, [
tsc,
"--noEmit",
"--strict",
"--target", "ES2022",
"--module", "CommonJS",
"--moduleResolution", "Node",
"--skipLibCheck",
"types-smoke.ts",
], { cwd: consumerRoot, stdio: "inherit" });
console.log("TypeScript package closure smoke: passed");
for (const tarball of tarballs) console.log(`Packed: ${tarball}`);
} finally {
rmSync(smokeRoot, { recursive: true, force: true });
}