/** * Tests for the npm publish surface contract. */ const assert = require("assert") const fs = require("fs") const path = require("path") const { spawnSync } = require("child_process") function runTest(name, fn) { try { fn() console.log(` ✓ ${name}`) return true } catch (error) { console.log(` ✗ ${name}`) console.error(` ${error.message}`) return false } } function normalizePublishPath(value) { return String(value).replace(/\\/g, "/").replace(/\/$/, "") } function isCoveredByAncestor(target, roots) { const parts = target.split("/") for (let index = 1; index < parts.length; index += 1) { const ancestor = parts.slice(0, index).join("/") if (roots.has(ancestor)) { return true } } return false } function buildExpectedPublishPaths(repoRoot) { const modules = JSON.parse( fs.readFileSync(path.join(repoRoot, "manifests", "install-modules.json"), "utf8") ).modules const extraPaths = [ "manifests", "scripts/ecc.js", "scripts/catalog.js", "scripts/claw.js", "scripts/doctor.js", "scripts/status.js", "scripts/sessions-cli.js", "scripts/install-apply.js", "scripts/install-plan.js", "scripts/list-installed.js", "scripts/skill-create-output.js", "scripts/repair.js", "scripts/harness-audit.js", "scripts/session-inspect.js", "scripts/uninstall.js", "scripts/gemini-adapt-agents.js", "scripts/codex/merge-codex-config.js", "scripts/codex/merge-mcp-config.js", ".codex-plugin", ".mcp.json", "install.sh", "install.ps1", "agent.yaml", "VERSION", ] const combined = new Set( [...modules.flatMap((module) => module.paths || []), ...extraPaths].map(normalizePublishPath) ) return [...combined] .filter((publishPath) => !isCoveredByAncestor(publishPath, combined)) .sort() } function main() { console.log("\n=== Testing npm publish surface ===\n") let passed = 0 let failed = 0 const repoRoot = path.join(__dirname, "..", "..") const packageJson = JSON.parse( fs.readFileSync(path.join(repoRoot, "package.json"), "utf8") ) const expectedPublishPaths = buildExpectedPublishPaths(repoRoot) const actualPublishPaths = packageJson.files.map(normalizePublishPath).sort() const tests = [ ["package.json files align to the module graph and explicit runtime allowlist", () => { assert.deepStrictEqual(actualPublishPaths, expectedPublishPaths) }], ["npm pack publishes the reduced runtime surface", () => { const result = spawnSync("npm", ["pack", "--dry-run", "--json"], { cwd: repoRoot, encoding: "utf8", shell: process.platform === "win32", }) assert.strictEqual(result.status, 0, result.error?.message || result.stderr) const packOutput = JSON.parse(result.stdout) const packagedPaths = new Set(packOutput[0]?.files?.map((file) => file.path) ?? []) for (const requiredPath of [ "scripts/catalog.js", ".gemini/GEMINI.md", ".claude-plugin/plugin.json", ".codex-plugin/plugin.json", "skills/backend-patterns/SKILL.md", ]) { assert.ok( packagedPaths.has(requiredPath), `npm pack should include ${requiredPath}` ) } for (const excludedPath of [ "contexts/dev.md", "examples/CLAUDE.md", "plugins/README.md", "schemas/hooks.schema.json", "scripts/ci/catalog.js", "skills/skill-comply/SKILL.md", ]) { assert.ok( !packagedPaths.has(excludedPath), `npm pack should not include ${excludedPath}` ) } }], ] for (const [name, fn] of tests) { if (runTest(name, fn)) { passed += 1 } else { failed += 1 } } console.log(`\nPassed: ${passed}`) console.log(`Failed: ${failed}`) process.exit(failed > 0 ? 1 : 0) } main()