@orkestrel/scaffold 0.0.62 → 0.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -103
- package/dist/bin/main.js +95 -27
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +2 -2
- package/dist/host/CLAUDE.md +6 -0
- package/dist/host/agents/orchestration.md +23 -15
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +16 -16
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +6 -6
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +4 -4
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
- package/dist/host/claude/agents/orkestrel.md +56 -56
- package/dist/host/claude/agents/reviewer.md +13 -0
- package/dist/host/claude/rules/architecture.md +51 -45
- package/dist/host/claude/rules/documentation.md +18 -1
- package/dist/host/claude/rules/portability.md +2 -0
- package/dist/host/claude/rules/quality.md +1 -1
- package/dist/host/claude/rules/tests.md +12 -11
- package/dist/host/claude/rules/typescript.md +5 -0
- package/dist/host/claude/rules/workspace.md +23 -18
- package/dist/host/claude/rules/writing.md +4 -0
- package/dist/host/codex/agents/orkestrel.toml +3 -3
- package/dist/host/codex/agents/reviewer.toml +4 -2
- package/dist/host/configs/helpers.ts +311 -2
- package/dist/host/configs/policy.ts +1100 -51
- package/dist/host/dotfiles/oxlintrc.json +72 -1
- package/dist/host/guides/guide.md +749 -222
- package/dist/host/guides/scaffold.md +472 -378
- package/dist/host/manifest.json +34 -33
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +1200 -16
- package/dist/host/tests/policy.test.ts +157 -173
- package/dist/host/tests/setupPolicy.ts +522 -1007
- package/dist/src/core/index.cjs +373 -279
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +130 -120
- package/dist/src/core/index.d.ts +130 -120
- package/dist/src/core/index.js +373 -278
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +28 -21
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +38 -33
- package/dist/src/server/index.d.ts +38 -33
- package/dist/src/server/index.js +28 -21
- package/dist/src/server/index.js.map +1 -1
- package/package.json +17 -18
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
2
|
-
import { EmitterHooks } from '@orkestrel/emitter';
|
|
3
|
-
import { EmitterInterface } from '@orkestrel/emitter';
|
|
4
|
-
import { Guard } from '@orkestrel/contract';
|
|
5
|
-
import { JSONValue } from '@orkestrel/contract';
|
|
1
|
+
import type { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
2
|
+
import type { EmitterHooks } from '@orkestrel/emitter';
|
|
3
|
+
import type { EmitterInterface } from '@orkestrel/emitter';
|
|
4
|
+
import type { Guard } from '@orkestrel/contract';
|
|
5
|
+
import type { JSONValue } from '@orkestrel/contract';
|
|
6
6
|
|
|
7
7
|
/** Lists the development dependencies a private Vue browser application adds. */
|
|
8
8
|
export declare const APP_BROWSER_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
@@ -101,13 +101,13 @@ export declare const ARTIFACT_TEMPLATES: Readonly<{
|
|
|
101
101
|
entry: "import * as entry from {{specifier}}\nimport { describe, expect, it } from 'vitest'\n\ndescribe({{label}}, () => {\n\tit('has no starter exports', () => {\n\t\texpect(Object.keys(entry)).toStrictEqual([])\n\t})\n})\n";
|
|
102
102
|
bin: "import { describe, expect, it } from 'vitest'\n\ndescribe('bin entry', () => {\n\tit('has no starter exports', async () => {\n{{import}}\n\t\texpect(Object.keys(entry)).toStrictEqual([])\n\t})\n})\n";
|
|
103
103
|
distribution: Readonly<{
|
|
104
|
-
proof: "// The artifact a consumer installs, measured rather than described. This workspace\n// is packed and installed into a throwaway consumer, and every following claim is read\n// off that installed tree: the exports map it publishes, the declarations it ships,\n// and the module objects a real runtime hands a consumer. Nothing here names this\n// package, one of its exports, or how many there are, so the proof stays true as\n// the published surface moves.\n{{types}}import type { SpawnSyncReturns } from 'node:child_process'\nimport type { TestContext } from 'vitest'\nimport { spawnSync } from 'node:child_process'\nimport {\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\treadFileSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from 'node:fs'\n{{transport}}import { tmpdir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n{{launcher}}import ts from 'typescript'\nimport { afterAll, describe, expect, it } from 'vitest'\n\nconst ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')\nconst NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'\n// Windows needs a shell to launch a `.cmd`: Node refuses one directly since the\n// batch-argument hardening, and `spawnSync` returns `EINVAL` with a null status\n// rather than an exit code a caller can read. Every following argument is a literal or\n// a path this file built, so the shell has nothing to escape.\nconst SHELL = process.platform === 'win32'\n// `prepublishOnly` runs this proof as `npm run test:distribution -- --mode release`.\n// Release is the publish gate, so evidence it cannot obtain fails there and skips\n// everywhere else: a gate that passes on missing evidence proves nothing.\nconst RELEASE = import.meta.env.MODE === 'release'\n// The built output directory convention a browser face may publish from. Every\n// selection reads this prefix off the export target and never off the subpath name. A\n// workspace whose only published face is the browser one publishes that face at the\n// root subpath, so a rule keyed on the subpath name drives a browser bundle through\n// Node and the miss is silent.\nconst BROWSER_OUTPUT = './dist/src/browser/'\nconst ABSENT_SUBPATH = '/no-subpath-is-published-under-this-name'\nconst PING = ['ping', '--fetch-retries=0', '--fetch-timeout=5000', '--loglevel=silent']\nconst ESM_DRIVER = 'drive.mjs'\nconst CJS_DRIVER = 'drive.cjs'\nconst CONSUMER_MANIFEST = `{ \"name\": \"distribution-consumer\", \"private\": true, \"type\": \"module\" }\\n`\nconst ESM_DRIVER_SOURCE = `const entry = await import(process.argv[2])\nprocess.stdout.write(JSON.stringify(Object.keys(entry).sort()))\n`\nconst CJS_DRIVER_SOURCE = `const entry = require(process.argv[2])\nprocess.stdout.write(JSON.stringify(Object.keys(entry).sort()))\n`\n\n// The extensions a JavaScript handler loads as modules. Node loads a native addon\n// through its addon handler instead, so that extension is named separately.\nconst MODULE_EXTENSIONS = ['.js', '.mjs', '.cjs']\nconst ADDON_EXTENSION = '.node'\n// The extensions a declaration file carries. A `require` condition declares\n// `.d.cts` and an ESM-only one `.d.mts`, so the `.d.ts` spelling alone does not\n// name them.\nconst DECLARATION_EXTENSIONS = ['.d.ts', '.d.cts', '.d.mts']\ntype Format = 'module' | 'commonjs'\n\n// The Node import target is resolved with the conditions that driver supplies. The\n// CommonJS compile probe is selected from its declaration's format, and its runtime\n// drive loads the same subpath through Node's require resolver. Vite's production\n// client build enables its module and browser conditions.\nconst RUNTIME_CONDITIONS = Object.freeze({\n\tmodule: Object.freeze(['node-addons', 'node', 'import', 'module-sync']),\n\tcommonjs: Object.freeze(['node-addons', 'node', 'require', 'module-sync']),\n\tbrowser: Object.freeze(['module', 'browser', 'production', 'import']),\n})\n// TypeScript's Node resolutions add `node` to the format condition. Its bundler\n// resolution does not, so a browser drive compares against the declaration a bundler\n// consumer reads rather than borrowing the Node declaration.\nconst BUNDLER_CONDITIONS = Object.freeze({\n\tmodule: ['types', 'import'],\n\tcommonjs: ['types', 'require'],\n})\nconst DECLARATION_CONDITIONS = Object.freeze({\n\tmodule: ['types', 'node', 'import'],\n\tcommonjs: ['types', 'node', 'require'],\n\tbrowser: BUNDLER_CONDITIONS.module,\n})\n\ninterface Resolution {\n\treadonly label: string\n\treadonly resolution: ts.ModuleResolutionKind\n\treadonly module: ts.ModuleKind\n\treadonly conditions: Readonly<Record<Format, readonly string[]>>\n}\n\ninterface TargetResolution {\n\treadonly target: string\n}\n\n// Each compile driver carries the conditions TypeScript applies for its resolution\n// and importing format. A `require`-only subpath therefore stays in each CommonJS\n// probe that can resolve it.\nconst RESOLUTIONS: readonly Resolution[] = [\n\t{\n\t\tlabel: 'node16',\n\t\tresolution: ts.ModuleResolutionKind.Node16,\n\t\tmodule: ts.ModuleKind.Node16,\n\t\tconditions: DECLARATION_CONDITIONS,\n\t},\n\t{\n\t\tlabel: 'nodenext',\n\t\tresolution: ts.ModuleResolutionKind.NodeNext,\n\t\tmodule: ts.ModuleKind.NodeNext,\n\t\tconditions: DECLARATION_CONDITIONS,\n\t},\n\t{\n\t\tlabel: 'bundler',\n\t\tresolution: ts.ModuleResolutionKind.Bundler,\n\t\tmodule: ts.ModuleKind.ESNext,\n\t\tconditions: BUNDLER_CONDITIONS,\n\t},\n]\n\nconst FORMATS: ReadonlyArray<readonly [extension: string, format: Format]> = [\n\t['ts', 'module'],\n\t['cts', 'commonjs'],\n]\n\n// One published subpath, resolved to what this proof can drive: the specifier a\n// consumer writes, the declarations its consumer formats name, whether its target\n// is a browser bundle, and whether it answers `import` and `require` at all.\ninterface Entry {\n\treadonly subpath: string\n\treadonly specifier: string\n\treadonly mapping: unknown\n\treadonly declaration: {\n\t\treadonly module: string | undefined\n\t\treadonly commonjs: string | undefined\n\t\treadonly browser: string | undefined\n\t}\n\treadonly browser: boolean\n\treadonly module: boolean\n\treadonly commonjs: boolean\n\treadonly required: boolean\n}\n\n// The installed tree every claim is read from. Every subpath the exports map names\n// lands in exactly one of `entries`, `undeclared`, and `excluded`, so a subpath this\n// proof cannot drive is reported rather than dropped.\ninterface Stage {\n\treadonly consumer: string\n\treadonly installed: string\n\treadonly archives: readonly string[]\n\treadonly entries: readonly Entry[]\n\treadonly subpaths: readonly string[]\n\treadonly undeclared: readonly string[]\n\treadonly excluded: readonly string[]\n\treadonly targets: readonly string[]\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isNames(value: unknown): value is readonly string[] {\n\treturn Array.isArray(value) && value.every((name) => typeof name === 'string')\n}\n\n// A fallback list, which is what Node reads an array in an exports entry as. The\n// narrowing is what the following walkers need: `Array.isArray` widens an `unknown`\n// member to `any`, and an entry read that way is not read at all.\nfunction isList(value: unknown): value is readonly unknown[] {\n\treturn Array.isArray(value)\n}\n\n// Whether a string is a valid package target. Node rejects a target outside the\n// package and a target containing a dot, parent, or node_modules segment during\n// package-target resolution. A later module-resolution failure is not the same\n// thing: an array falls through the former and keeps the latter.\nfunction isPackageTarget(target: string): boolean {\n\tif (!target.startsWith('./')) return false\n\tfor (const segment of target.slice(2).split(/[\\\\/]/u)) {\n\t\tlet decoded = segment\n\t\ttry {\n\t\t\tdecoded = decodeURIComponent(segment)\n\t\t} catch {}\n\t\tconst normalized = decoded.toLowerCase()\n\t\tif (normalized === '.' || normalized === '..' || normalized === 'node_modules') return false\n\t}\n\treturn true\n}\n\nfunction readJson(path: string): unknown {\n\tconst parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n\treturn parsed\n}\n\nfunction readManifestName(path: string): string {\n\tconst manifest = readJson(path)\n\tif (!isRecord(manifest) || typeof manifest.name !== 'string') {\n\t\tthrow new Error(`The manifest at ${path} declares no package name`)\n\t}\n\treturn manifest.name\n}\n\nfunction writeFile(path: string, content: string): void {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, content)\n}\n\nfunction readOutput(result: SpawnSyncReturns<string>): string {\n\treturn `${result.stdout ?? ''}${result.stderr ?? ''}`.trim()\n}\n\nfunction runNpm(args: readonly string[], cwd: string): SpawnSyncReturns<string> {\n\treturn spawnSync(NPM, [...args], {\n\t\tcwd,\n\t\tencoding: 'utf8',\n\t\tenv: { ...process.env, npm_config_cache: CACHE },\n\t\tshell: SHELL,\n\t\twindowsHide: true,\n\t})\n}\n\nfunction runNode(args: readonly string[], cwd: string): SpawnSyncReturns<string> {\n\treturn spawnSync(process.execPath, [...args], { cwd, encoding: 'utf8', windowsHide: true })\n}\n\n// Node's own condition matching, read in declaration order.\nfunction resolvePackageTarget(\n\tentry: unknown,\n\tconditions: readonly string[],\n): TargetResolution | undefined {\n\tif (typeof entry === 'string') return { target: entry }\n\tif (isList(entry)) {\n\t\tfor (const member of entry) {\n\t\t\tconst resolved = resolvePackageTarget(member, conditions)\n\t\t\tif (resolved !== undefined && isPackageTarget(resolved.target)) return resolved\n\t\t}\n\t\treturn undefined\n\t}\n\tif (!isRecord(entry)) return undefined\n\tfor (const [condition, nested] of Object.entries(entry)) {\n\t\tif (condition !== 'default' && !conditions.includes(condition)) continue\n\t\tconst resolved = resolvePackageTarget(nested, conditions)\n\t\tif (resolved !== undefined) return resolved\n\t}\n\treturn undefined\n}\n\n// A flat entry, a condition-nested entry, and a fallback list all resolve through\n// one walker. An entry may declare `types` beside `default` at its top level\n// rather than inside `import`, so a fixed `entry.import.types` lookup is not\n// equivalent to condition resolution.\nfunction resolveTarget(entry: unknown, conditions: readonly string[]): string | undefined {\n\treturn resolvePackageTarget(entry, conditions)?.target\n}\n\n// Whether a path is a physical file. TypeScript's file-existence check refuses a\n// directory at the same spelling and continues to the outer package scope.\nfunction matchesFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile()\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// TypeScript resolves a declaration target by accepting an existing declaration\n// directly or by substituting beside a JavaScript target. A missing target leaves\n// the containing condition or fallback list unresolved, so the walk continues.\nfunction targetToDeclaration(target: string, installed: string): string | undefined {\n\tif (!isPackageTarget(target)) return undefined\n\tlet declaration = target\n\tif (target.endsWith('.cjs')) declaration = `${target.slice(0, -4)}.d.cts`\n\telse if (target.endsWith('.mjs')) declaration = `${target.slice(0, -4)}.d.mts`\n\telse if (target.endsWith('.js')) declaration = `${target.slice(0, -3)}.d.ts`\n\telse if (!isDeclaration(target)) return undefined\n\treturn matchesFile(join(installed, declaration)) ? declaration : undefined\n}\n\n// The declaration TypeScript resolves through one importing format's conditions.\n// Condition objects keep manifest order, and arrays keep fallback order.\nfunction resolveDeclaration(\n\tentry: unknown,\n\tconditions: readonly string[],\n\tinstalled: string,\n): string | undefined {\n\tif (typeof entry === 'string') return targetToDeclaration(entry, installed)\n\tif (isList(entry)) {\n\t\tfor (const member of entry) {\n\t\t\tconst resolved = resolveDeclaration(member, conditions, installed)\n\t\t\tif (resolved !== undefined) return resolved\n\t\t}\n\t\treturn undefined\n\t}\n\tif (!isRecord(entry)) return undefined\n\tfor (const [condition, nested] of Object.entries(entry)) {\n\t\tif (condition !== 'default' && !conditions.includes(condition)) continue\n\t\tconst resolved = resolveDeclaration(nested, conditions, installed)\n\t\tif (resolved !== undefined) return resolved\n\t}\n\treturn undefined\n}\n\n// The nearest package scope that decides a `.d.ts` declaration's module format. A\n// physical nested manifest starts a scope even when it omits `type` or cannot be\n// parsed. A directory at that spelling is not a manifest, so the walk continues.\nfunction readPackageType(installed: string, target: string): unknown {\n\tlet directory = dirname(join(installed, target))\n\twhile (true) {\n\t\tconst path = join(directory, 'package.json')\n\t\tif (matchesFile(path)) {\n\t\t\ttry {\n\t\t\t\tconst manifest = readJson(path)\n\t\t\t\treturn isRecord(manifest) ? manifest.type : undefined\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\tif (directory === installed) return undefined\n\t\tconst parent = dirname(directory)\n\t\tif (parent === directory) return undefined\n\t\tdirectory = parent\n\t}\n}\n\nfunction resolvesBrowser(entry: unknown): boolean {\n\tconst module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)\n\tif (module !== undefined && module.startsWith(BROWSER_OUTPUT)) return true\n\tif (module === undefined) return false\n\tconst imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)\n\tconst required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\treturn module !== imported && module !== required\n}\n\n// Whether the target selected by Node's CommonJS conditions is a module require can\n// load. A JavaScript target takes its own nearest package scope. Native addons and\n// extensionless targets have their own CommonJS handlers.\nfunction resolvesCommonJS(entry: unknown, installed: string): boolean {\n\tconst target = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\tif (target === undefined) return false\n\tconst name = target.slice(target.lastIndexOf('/') + 1)\n\tif (name.endsWith('.cjs')) return true\n\tif (name.endsWith('.mjs')) return false\n\tif (name.endsWith('.node')) return true\n\tif (!name.includes('.')) return true\n\treturn name.endsWith('.js') && readPackageType(installed, target) !== 'module'\n}\n\n// Whether the declaration selected by a typed CommonJS consumer admits that entry.\n// A `.d.cts` declaration admits and a `.d.mts` declaration refuses. A `.d.ts`\n// declaration takes its own nearest package scope.\nfunction declaresCommonJS(entry: unknown, installed: string): boolean {\n\tconst declaration = resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed)\n\tif (declaration === undefined) return false\n\tif (declaration.endsWith('.d.cts')) return true\n\tif (declaration.endsWith('.d.mts')) return false\n\treturn declaration.endsWith('.d.ts') && readPackageType(installed, declaration) !== 'module'\n}\n\n// Every target an entry names under any condition. A fallback list omits members\n// Node rejects during package-target validation, because no reader can take them.\nfunction collectTargets(entry: unknown): readonly string[] {\n\tif (typeof entry === 'string') return [entry]\n\tif (isList(entry)) return entry.flatMap(collectTargets).filter(isPackageTarget)\n\tif (!isRecord(entry)) return []\n\treturn Object.values(entry).flatMap((nested) => collectTargets(nested))\n}\n\n// Whether a target is a file a runtime loads for its names, which is what a\n// declaration is owed for. The extension on the target's own file name decides it,\n// and a name carrying no extension is code: `require` reads such a file through its\n// JavaScript handler, so an extensionless target loads and publishes names. Node\n// loads `.node` through its native-addon handler. Every other extension is an asset\n// a consumer reads rather than imports — a stylesheet, a WebAssembly binary, the\n// `\"./package.json\"` manifest pointer, and a declaration alike.\n// The cost is an extensionless file published for a reader, such as a `LICENSE`:\n// that target reports undeclared until it is given an extension or a declaration.\nfunction isModule(target: string): boolean {\n\tconst name = target.slice(target.lastIndexOf('/') + 1)\n\tconst dot = name.lastIndexOf('.')\n\tif (name.endsWith(ADDON_EXTENSION)) return true\n\treturn dot === -1 || MODULE_EXTENSIONS.includes(name.slice(dot))\n}\n\n// Whether a resolved target is a declaration rather than the JavaScript a\n// `default` branch answers with when the entry declares no `types` condition.\nfunction isDeclaration(target: string): boolean {\n\treturn DECLARATION_EXTENSIONS.some((extension) => target.endsWith(extension))\n}\n\n// The declarations the Node module, Node CommonJS, and browser drives compare\n// against. Each field uses the conditions of the TypeScript consumer paired with\n// that runtime. A JavaScript target resolves through TypeScript's adjacent\n// declaration substitution rather than standing in for the declaration itself.\nfunction readDeclaration(entry: unknown, installed: string): Entry['declaration'] {\n\treturn {\n\t\tmodule: resolveDeclaration(entry, DECLARATION_CONDITIONS.module, installed),\n\t\tcommonjs: resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed),\n\t\tbrowser: resolveDeclaration(entry, DECLARATION_CONDITIONS.browser, installed),\n\t}\n}\n\n// The entries one compile driver can resolve under its own conditions.\nfunction selectEntries(entries: readonly Entry[], conditions: readonly string[]): readonly Entry[] {\n\treturn entries.filter(\n\t\t(entry) =>\n\t\t\tresolveTarget(entry.mapping, conditions) !== undefined &&\n\t\t\t(!conditions.includes('require') || entry.commonjs),\n\t)\n}\n\n// Require-loadable entries that declare CommonJS support but a typed CommonJS\n// consumer cannot compile against. A default branch resolving under the require\n// condition set makes no CommonJS claim.\nfunction selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {\n\treturn entries.filter(\n\t\t(entry) =>\n\t\t\tentry.required &&\n\t\t\tisRecord(entry.mapping) &&\n\t\t\tObject.hasOwn(entry.mapping, 'require') &&\n\t\t\t!declaresCommonJS(entry.mapping, installed),\n\t)\n}\n\n// The value exports a declaration publishes, read through the compiler's checker\n// over the module symbol rather than off the declaration text. An alias resolves to\n// what it names, so a re-export counts as the thing it re-exports, and a type-only\n// symbol is dropped because no runtime publishes one.\nfunction readDeclaredExports(declaration: string): readonly string[] {\n\tconst program = ts.createProgram([declaration], {\n\t\tmodule: ts.ModuleKind.ESNext,\n\t\tmoduleResolution: ts.ModuleResolutionKind.Bundler,\n\t\tnoEmit: true,\n\t\tskipLibCheck: true,\n\t\ttarget: ts.ScriptTarget.ESNext,\n\t})\n\tconst source = program.getSourceFile(declaration)\n\tif (source === undefined) throw new Error(`The declaration ${declaration} was not read`)\n\tconst checker = program.getTypeChecker()\n\tconst symbol = checker.getSymbolAtLocation(source)\n\tif (symbol === undefined) throw new Error(`${declaration} declares no module symbol`)\n\tconst values: string[] = []\n\tfor (const exported of checker.getExportsOfModule(symbol)) {\n\t\tconst direct = (exported.flags & ts.SymbolFlags.Alias) === 0\n\t\tconst resolved = direct ? exported : checker.getAliasedSymbol(exported)\n\t\tif ((resolved.flags & ts.SymbolFlags.Value) !== 0) values.push(exported.getName())\n\t}\n\treturn [...values].sort()\n}\n\n// The diagnostics a consumer compiling against the installed declarations reports,\n// flattened to their messages so a failure names what the consumer could not do.\nfunction compileConsumer(\n\tentry: string,\n\tresolution: ts.ModuleResolutionKind,\n\tmodule: ts.ModuleKind,\n): readonly string[] {\n\tconst program = ts.createProgram([entry], {\n\t\tmodule,\n\t\tmoduleResolution: resolution,\n\t\tnoEmit: true,\n\t\tskipLibCheck: true,\n\t\tstrict: true,\n\t\ttarget: ts.ScriptTarget.ESNext,\n\t})\n\treturn ts\n\t\t.getPreEmitDiagnostics(program)\n\t\t.map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, ' '))\n}\n\n// One consumer module importing every installed entry, written where its own\n// resolution finds the installed package.\nfunction writeConsumerProbe(stage: Stage, path: string, specifiers: readonly string[]): string {\n\tconst names: string[] = []\n\tconst bindings: string[] = []\n\tfor (const [index, specifier] of specifiers.entries()) {\n\t\tconst binding = `entry${String(index)}`\n\t\tnames.push(binding)\n\t\tbindings.push(`import * as ${binding} from ${JSON.stringify(specifier)}`)\n\t}\n\tconst target = join(stage.consumer, path)\n\twriteFile(target, `${bindings.join('\\n')}\\nexport const surface = [${names.join(', ')}]\\n`)\n\treturn target\n}\n\n// The runtime key set a real process reads off one installed entry under one\n// condition. The driver is a file rather than an `--eval` string, so the specifier\n// travels as an argument and nothing needs escaping.\nfunction driveRuntime(stage: Stage, specifier: string, driver: string): readonly string[] {\n\tconst result = runNode([join(stage.consumer, driver), specifier], stage.consumer)\n\tif (result.status !== 0) {\n\t\tthrow new Error(`Loading ${specifier} from the consumer failed: ${readOutput(result)}`)\n\t}\n\tconst published: unknown = JSON.parse(result.stdout)\n\tif (!isNames(published)) throw new Error(`The driver printed no name list for ${specifier}`)\n\treturn published\n}\n{{helpers}}\n// Pack this workspace, install the archive into an isolated consumer, and read the\n// published surface back off the installed tree. Every later claim reads this\n// result, so a failure here is raised where it happens rather than once per entry.\nfunction buildStage(): Stage {\n\tconst packed = join(SCRATCH, 'packed')\n\tconst consumer = join(SCRATCH, 'consumer')\n\tmkdirSync(packed, { recursive: true })\n\tconst pack = runNpm(['pack', '--ignore-scripts', '--pack-destination', packed], ROOT)\n\tif (pack.status !== 0) throw new Error(`npm pack refused this workspace: ${readOutput(pack)}`)\n\tconst archives = readdirSync(packed).filter((name) => name.endsWith('.tgz'))\n\tconst archive = archives[0]\n\tif (archives.length !== 1 || archive === undefined) {\n\t\tthrow new Error(`npm pack wrote no single archive: ${archives.join(', ')}`)\n\t}\n\twriteFile(join(consumer, 'package.json'), CONSUMER_MANIFEST)\n\twriteFile(join(consumer, ESM_DRIVER), ESM_DRIVER_SOURCE)\n\twriteFile(join(consumer, CJS_DRIVER), CJS_DRIVER_SOURCE)\n\tconst install = runNpm(\n\t\t['install', '--ignore-scripts', '--no-audit', '--no-fund', join(packed, archive)],\n\t\tconsumer,\n\t)\n\tif (install.status !== 0) {\n\t\tthrow new Error(`Installing the packed archive failed: ${readOutput(install)}`)\n\t}\n\tconst name = readManifestName(join(ROOT, 'package.json'))\n\tconst installed = join(consumer, 'node_modules', ...name.split('/'))\n\tconst manifest = readJson(join(installed, 'package.json'))\n\tif (!isRecord(manifest) || !isRecord(manifest.exports)) {\n\t\tthrow new Error('The installed manifest publishes no exports map')\n\t}\n\tconst entries: Entry[] = []\n\tconst targets: string[] = []\n\tconst subpaths: string[] = []\n\tconst undeclared: string[] = []\n\tconst excluded: string[] = []\n\tfor (const [subpath, entry] of Object.entries(manifest.exports)) {\n\t\tconst files = collectTargets(entry)\n\t\ttargets.push(...files)\n\t\tsubpaths.push(subpath)\n\t\tconst declaration = readDeclaration(entry, installed)\n\t\t// A subpath resolving no declaration is partitioned rather than dropped. It is a\n\t\t// defect when a runtime loads one of its targets for names, because a consumer\n\t\t// importing it compiles against nothing under `node16`. It is an excluded\n\t\t// publication otherwise: the `\"./package.json\"` manifest pointer and a stylesheet\n\t\t// are published for a reader rather than an importer.\n\t\tif (\n\t\t\tdeclaration.module === undefined &&\n\t\t\tdeclaration.commonjs === undefined &&\n\t\t\tdeclaration.browser === undefined\n\t\t) {\n\t\t\tif (files.some(isModule)) undeclared.push(subpath)\n\t\t\telse excluded.push(subpath)\n\t\t\tcontinue\n\t\t}\n\t\tconst imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)\n\t\tconst requiredTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\t\tconst browserTarget = resolveTarget(entry, RUNTIME_CONDITIONS.browser)\n\t\tconst browser = resolvesBrowser(entry)\n\t\tconst required = requiredTarget !== undefined && !(browser && requiredTarget === browserTarget)\n\t\tconst commonjs = required && resolvesCommonJS(entry, installed)\n\t\tentries.push({\n\t\t\tsubpath,\n\t\t\tspecifier: subpath === '.' ? name : `${name}${subpath.slice(1)}`,\n\t\t\tmapping: entry,\n\t\t\tdeclaration: {\n\t\t\t\tmodule: declaration.module === undefined ? undefined : join(installed, declaration.module),\n\t\t\t\tcommonjs:\n\t\t\t\t\tdeclaration.commonjs === undefined ? undefined : join(installed, declaration.commonjs),\n\t\t\t\tbrowser:\n\t\t\t\t\tdeclaration.browser === undefined ? undefined : join(installed, declaration.browser),\n\t\t\t},\n\t\t\tbrowser,\n\t\t\tmodule: imported !== undefined && !(browser && imported === browserTarget),\n\t\t\tcommonjs,\n\t\t\trequired,\n\t\t})\n\t}\n\treturn { consumer, installed, archives, entries, subpaths, undeclared, excluded, targets }\n}\n\nconst SCRATCH = mkdtempSync(join(tmpdir(), 'distribution-'))\nconst CACHE = join(SCRATCH, 'cache')\nmkdirSync(CACHE, { recursive: true })\n// The scratch tree holds the npm cache, the packed archive, and the installed\n// consumer, so its removal is registered before the first thing that can throw.\nafterAll(() => {\n\trmSync(SCRATCH, { force: true, recursive: true })\n})\n\n// Installing the packed archive resolves its own runtime dependencies, so an\n// unreachable registry leaves nothing to measure. Under release that is the gate\n// failing; anywhere else the suite skips and names the mechanism it wanted.\n//\n// A module that throws while loading never reaches the `afterAll` it registered,\n// so every throw here removes the scratch tree on its way out.\nfunction openStage(): Stage | undefined {\n\ttry {\n\t\tif (runNpm(PING, ROOT).status !== 0) {\n\t\t\tif (!RELEASE) return undefined\n\t\t\tthrow new Error(\n\t\t\t\t'The release gate requires a reachable npm registry, and npm ping did not answer',\n\t\t\t)\n\t\t}\n\t\treturn buildStage()\n\t} catch (error) {\n\t\trmSync(SCRATCH, { force: true, recursive: true })\n\t\tthrow error\n\t}\n}\n\nconst STAGE = openStage()\nconst STAGED = STAGE !== undefined\n\ndescribe('distribution classifiers', () => {\n\tit('classifies synthetic export mappings without a registry stage', () => {\n\t\tconst root = join(SCRATCH, 'classifiers')\n\t\twriteFile(\n\t\t\tjoin(root, 'package.json'),\n\t\t\tJSON.stringify({\n\t\t\t\ttype: 'commonjs',\n\t\t\t\texports: {\n\t\t\t\t\tcondition: { browser: './b.js', default: './n.js' },\n\t\t\t\t\tconvention: { default: './dist/src/browser/index.js' },\n\t\t\t\t\tuniversal: { default: './shared.js' },\n\t\t\t\t\t'import-shared': {\n\t\t\t\t\t\tbrowser: './shared.mjs',\n\t\t\t\t\t\timport: './shared.mjs',\n\t\t\t\t\t\tdefault: './node.js',\n\t\t\t\t\t},\n\t\t\t\t\t'require-shared': {\n\t\t\t\t\t\tbrowser: './shared.cjs',\n\t\t\t\t\t\trequire: './shared.cjs',\n\t\t\t\t\t\tdefault: './node.js',\n\t\t\t\t\t},\n\t\t\t\t\tnode: { node: './node.js', default: './node.js' },\n\t\t\t\t\tsilent: { 'module-sync': './x.cjs', import: './x.mjs' },\n\t\t\t\t\tmodule: { require: './x.mjs' },\n\t\t\t\t\t'nested-module': { require: './module/x.js' },\n\t\t\t\t\t'nested-commonjs': { require: './commonjs/x.js' },\n\t\t\t\t\tesm: { import: './x.mjs' },\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\t\twriteFile(join(root, 'module/package.json'), '{ \"type\": \"module\" }\\n')\n\t\twriteFile(join(root, 'commonjs/package.json'), '{ \"type\": \"commonjs\" }\\n')\n\t\tconst manifest = readJson(join(root, 'package.json'))\n\t\tif (!isRecord(manifest) || !isRecord(manifest.exports)) {\n\t\t\tthrow new Error('The classifier fixture declares no exports map')\n\t\t}\n\t\tconst mappings = manifest.exports\n\t\texpect({\n\t\t\tcondition: resolvesBrowser(mappings.condition),\n\t\t\tconvention: resolvesBrowser(mappings.convention),\n\t\t\tuniversal: resolvesBrowser(mappings.universal),\n\t\t\timport: resolvesBrowser(mappings['import-shared']),\n\t\t\trequire: resolvesBrowser(mappings['require-shared']),\n\t\t\tnode: resolvesBrowser(mappings.node),\n\t\t}).toStrictEqual({\n\t\t\tcondition: true,\n\t\t\tconvention: true,\n\t\t\tuniversal: false,\n\t\t\timport: false,\n\t\t\trequire: false,\n\t\t\tnode: false,\n\t\t})\n\t\texpect({\n\t\t\tsilent: resolvesCommonJS(mappings.silent, root),\n\t\t\tmodule: resolvesCommonJS(mappings.module, root),\n\t\t\tnestedModule: resolvesCommonJS(mappings['nested-module'], root),\n\t\t\tnestedCommonJS: resolvesCommonJS(mappings['nested-commonjs'], root),\n\t\t\tesm: resolvesCommonJS(mappings.esm, root),\n\t\t}).toStrictEqual({\n\t\t\tsilent: true,\n\t\t\tmodule: false,\n\t\t\tnestedModule: false,\n\t\t\tnestedCommonJS: true,\n\t\t\tesm: false,\n\t\t})\n\t})\n})\n\n// The staged consumer, or a skip naming what the run could not reach. `it.skipIf`\n// carries no reason, so the gate sits here where the test context can state one.\nfunction requireStage(context: TestContext): Stage {\n\tif (!STAGED) {\n\t\treturn context.skip('`npm ping` did not answer, so nothing was packed or installed')\n\t}\n\treturn STAGE\n}\n\ndescribe('installed package consumer', () => {\n\tit('packs one archive and installs it in isolation [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\texpect(stage.archives).toHaveLength(1)\n\t\texpect(existsSync(join(stage.installed, 'package.json'))).toBe(true)\n\t\texpect(stage.entries.length).toBeGreaterThan(0)\n\t})\n\n\tit('ships every relative target its exports map names [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst relative = stage.targets.filter((target) => target.startsWith('./'))\n\t\texpect(relative).not.toStrictEqual([])\n\t\texpect(relative.filter((target) => !existsSync(join(stage.installed, target)))).toStrictEqual(\n\t\t\t[],\n\t\t)\n\t})\n\n\t// Every published subpath is driven, excluded by name, or reported here. A dropped\n\t// one leaves no trace: no runtime test, no declaration comparison, and no place in\n\t// the resolution compile, so the run reports success for a subpath it never\n\t// measured.\n\tit('declares types for every module it publishes [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst partitioned = [\n\t\t\t...stage.entries.map((entry) => entry.subpath),\n\t\t\t...stage.undeclared,\n\t\t\t...stage.excluded,\n\t\t]\n\t\texpect(stage.undeclared).toStrictEqual([])\n\t\texpect(partitioned.sort()).toStrictEqual([...stage.subpaths].sort())\n\t\t// A driven subpath answers a runtime condition. One resolving a declaration and\n\t\t// no Node or browser target compiles for a consumer and throws when that consumer\n\t\t// loads it. Each later drive retires itself for that entry, so this assertion names\n\t\t// the subpath rather than counting it as driven.\n\t\tconst unreachable = stage.entries.filter(\n\t\t\t(entry) => !entry.module && !entry.required && !entry.browser,\n\t\t)\n\t\texpect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])\n\t\tconst untypable = selectUntypable(stage.entries, stage.installed)\n\t\texpect(untypable.map((entry) => entry.subpath)).toStrictEqual([])\n\t})\n\n\tit('refuses a subpath its exports map does not name [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst name = readManifestName(join(stage.installed, 'package.json'))\n\t\tconst driver = join(stage.consumer, ESM_DRIVER)\n\t\tconst result = runNode([driver, `${name}${ABSENT_SUBPATH}`], stage.consumer)\n\t\texpect(result.status).not.toBe(0)\n\t\texpect(readOutput(result)).toContain('ERR_PACKAGE_PATH_NOT_EXPORTED')\n\t})\n\n\t// The absent subpath is the firing control: a resolution that reports nothing\n\t// for every published entry has not been shown to resolve anything at all. Each\n\t// module format carries its own control, because a format that resolves nothing\n\t// is silent for the same reason a resolution that resolves nothing is.\n\tit('compiles a consumer under every module resolution [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst name = readManifestName(join(stage.installed, 'package.json'))\n\t\tconst reported: string[] = []\n\t\tconst silent: string[] = []\n\t\tfor (const driver of RESOLUTIONS) {\n\t\t\tfor (const [extension, format] of FORMATS) {\n\t\t\t\tconst written = selectEntries(stage.entries, driver.conditions[format])\n\t\t\t\tif (written.length === 0) continue\n\t\t\t\tconst specifiers = written.map((entry) => entry.specifier)\n\t\t\t\tconst probe = writeConsumerProbe(stage, `probe.${driver.label}.${extension}`, specifiers)\n\t\t\t\tfor (const message of compileConsumer(probe, driver.resolution, driver.module)) {\n\t\t\t\t\treported.push(`${driver.label}.${extension}: ${message}`)\n\t\t\t\t}\n\t\t\t\tconst absent = [`${name}${ABSENT_SUBPATH}`]\n\t\t\t\tconst control = writeConsumerProbe(stage, `control.${driver.label}.${extension}`, absent)\n\t\t\t\tif (compileConsumer(control, driver.resolution, driver.module).length === 0) {\n\t\t\t\t\tsilent.push(`${driver.label}.${extension}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\texpect(reported).toStrictEqual([])\n\t\texpect(silent).toStrictEqual([])\n\t})\n{{guard}}})\n\nfor (const entry of STAGE?.entries ?? []) {\n\tdescribe(`installed entry ${entry.subpath}`, () => {\n\t\tit.runIf(entry.module)(\n\t\t\t'publishes what it declares to a Node import, and no more',\n\t\t\t(context) => {\n\t\t\t\tconst declaration = entry.declaration.module\n\t\t\t\tif (declaration === undefined) {\n\t\t\t\t\tthrow new Error(`${entry.subpath} publishes no import declaration`)\n\t\t\t\t}\n\t\t\t\tconst published = driveRuntime(requireStage(context), entry.specifier, ESM_DRIVER)\n\t\t\t\texpect(published).toStrictEqual(readDeclaredExports(declaration))\n\t\t\t},\n\t\t)\n\n\t\tit.runIf(entry.required)(\n\t\t\t'publishes what it declares to a Node require, and no more',\n\t\t\t(context) => {\n\t\t\t\tconst declaration = entry.declaration.commonjs\n\t\t\t\tif (declaration === undefined) {\n\t\t\t\t\tthrow new Error(`${entry.subpath} publishes no require declaration`)\n\t\t\t\t}\n\t\t\t\tconst published = driveRuntime(requireStage(context), entry.specifier, CJS_DRIVER)\n\t\t\t\texpect(published).toStrictEqual(readDeclaredExports(declaration))\n\t\t\t},\n\t\t)\n{{drive}}\t})\n}\n";
|
|
104
|
+
proof: "// The artifact a consumer installs, measured rather than described. This workspace\n// is packed and installed into a throwaway consumer, and every following claim is read\n// off that installed tree: the exports map it publishes, the declarations it ships,\n// and the module objects a real runtime hands a consumer. Nothing here names this\n// package, one of its exports, or how many there are, so the proof stays true as\n// the published surface moves.\n{{types}}import type { SpawnSyncReturns } from 'node:child_process'\nimport type { TestContext } from 'vitest'\nimport { spawnSync } from 'node:child_process'\nimport {\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\treadFileSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from 'node:fs'\n{{transport}}import { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n{{launcher}}import { afterAll, describe, expect, it } from 'vitest'\n\nconst ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')\nconst NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'\n// The compiler this workspace installs, run as a command rather than called in\n// process: the command and its plain-text diagnostics are the same across the\n// compiler majors this toolchain supports, and its in-process API is not. It is\n// resolved from the workspace under proof, so a consumer of the packed artifact is\n// checked by the same compiler that workspace's own `check` script runs.\nconst TSC = createRequire(join(ROOT, 'package.json')).resolve('typescript/bin/tsc')\n// Windows needs a shell to launch a `.cmd`: Node refuses one directly since the\n// batch-argument hardening, and `spawnSync` returns `EINVAL` with a null status\n// rather than an exit code a caller can read. Every following argument is a literal or\n// a path this file built, so the shell has nothing to escape.\nconst SHELL = process.platform === 'win32'\n// `prepublishOnly` runs this proof as `npm run test:distribution -- --mode release`.\n// Release is the publish gate, so evidence it cannot obtain fails there and skips\n// everywhere else: a gate that passes on missing evidence proves nothing.\nconst RELEASE = import.meta.env.MODE === 'release'\n// The built output directory convention a browser face may publish from. Every\n// selection reads this prefix off the export target and never off the subpath name. A\n// workspace whose only published face is the browser one publishes that face at the\n// root subpath, so a rule keyed on the subpath name drives a browser bundle through\n// Node and the miss is silent.\nconst BROWSER_OUTPUT = './dist/src/browser/'\nconst ABSENT_SUBPATH = '/no-subpath-is-published-under-this-name'\n// A compiler diagnostic that says where it is: the path relative to the directory\n// the compiler ran in, the 1-based line and column, the code, and the message. The\n// diagnostics are the verdict rather than the exit code, which differs between the\n// compiler majors this toolchain supports, so a reported line matching nothing here\n// came from something other than a check of a consumer module.\nconst DIAGNOSTIC_PATTERN = /^(.+?)\\(\\d+,\\d+\\): error TS\\d+: /u\nconst PING = ['ping', '--fetch-retries=0', '--fetch-timeout=5000', '--loglevel=silent']\nconst ESM_DRIVER = 'drive.mjs'\nconst CJS_DRIVER = 'drive.cjs'\nconst CONSUMER_MANIFEST = `{ \"name\": \"distribution-consumer\", \"private\": true, \"type\": \"module\" }\\n`\nconst ESM_DRIVER_SOURCE = `const entry = await import(process.argv[2])\nprocess.stdout.write(JSON.stringify(Object.keys(entry).sort()))\n`\nconst CJS_DRIVER_SOURCE = `const entry = require(process.argv[2])\nprocess.stdout.write(JSON.stringify(Object.keys(entry).sort()))\n`\n\n// The extensions a JavaScript handler loads as modules. Node loads a native addon\n// through its addon handler instead, so that extension is named separately.\nconst MODULE_EXTENSIONS = ['.js', '.mjs', '.cjs']\nconst ADDON_EXTENSION = '.node'\n// The extensions a declaration file carries. A `require` condition declares\n// `.d.cts` and an ESM-only one `.d.mts`, so the `.d.ts` spelling alone does not\n// name them.\nconst DECLARATION_EXTENSIONS = ['.d.ts', '.d.cts', '.d.mts']\ntype Format = 'module' | 'commonjs'\n\n// The Node import target is resolved with the conditions that driver supplies. The\n// CommonJS compile probe is selected from its declaration's format, and its runtime\n// drive loads the same subpath through Node's require resolver. Vite's production\n// client build enables its module and browser conditions.\nconst RUNTIME_CONDITIONS = Object.freeze({\n\tmodule: Object.freeze(['node-addons', 'node', 'import', 'module-sync']),\n\tcommonjs: Object.freeze(['node-addons', 'node', 'require', 'module-sync']),\n\tbrowser: Object.freeze(['module', 'browser', 'production', 'import']),\n})\n// TypeScript's Node resolutions add `node` to the format condition. Its bundler\n// resolution does not, so a browser drive compares against the declaration a bundler\n// consumer reads rather than borrowing the Node declaration.\nconst BUNDLER_CONDITIONS = Object.freeze({\n\tmodule: ['types', 'import'],\n\tcommonjs: ['types', 'require'],\n})\nconst DECLARATION_CONDITIONS = Object.freeze({\n\tmodule: ['types', 'node', 'import'],\n\tcommonjs: ['types', 'node', 'require'],\n\tbrowser: BUNDLER_CONDITIONS.module,\n})\n\ninterface Resolution {\n\treadonly label: string\n\treadonly resolution: string\n\treadonly module: string\n\treadonly conditions: Readonly<Record<Format, readonly string[]>>\n}\n\ninterface TargetResolution {\n\treadonly target: string\n}\n\n// Each compile driver carries the compiler options its scratch project sets and\n// the conditions TypeScript applies for that resolution and importing format. A\n// `require`-only subpath therefore stays in each CommonJS probe that can resolve it.\n// The option values are the spellings the project file takes, so nothing here needs\n// the compiler's own API to name them.\nconst RESOLUTIONS: readonly Resolution[] = [\n\t{\n\t\tlabel: 'node16',\n\t\tresolution: 'node16',\n\t\tmodule: 'node16',\n\t\tconditions: DECLARATION_CONDITIONS,\n\t},\n\t{\n\t\tlabel: 'nodenext',\n\t\tresolution: 'nodenext',\n\t\tmodule: 'nodenext',\n\t\tconditions: DECLARATION_CONDITIONS,\n\t},\n\t{\n\t\tlabel: 'bundler',\n\t\tresolution: 'bundler',\n\t\tmodule: 'esnext',\n\t\tconditions: BUNDLER_CONDITIONS,\n\t},\n]\n\n// The driver a bundled consumer reads declarations under. A browser application\n// compiles through a bundler, so the browser drive answers under this one alone,\n// and naming it here is what keeps that selection tied to the driver it selects.\nconst BROWSER_DRIVER = RESOLUTIONS.find((candidate) => candidate.label === 'bundler')\nif (BROWSER_DRIVER === undefined) throw new Error(\"RESOLUTIONS carries no 'bundler' row\")\n\nconst FORMATS: ReadonlyArray<readonly [extension: string, format: Format]> = [\n\t['ts', 'module'],\n\t['cts', 'commonjs'],\n]\n\n// One published subpath, resolved to what this proof can drive: the specifier a\n// consumer writes, whether the declarations its consumer formats resolve at all,\n// whether the exports map answers the browser condition with a target of its own,\n// whether it answers `import` and `require` at all, and whether the target that\n// `require` answers with is one that a CommonJS consumer loads.\ninterface Entry {\n\treadonly subpath: string\n\treadonly specifier: string\n\treadonly mapping: unknown\n\treadonly declaration: {\n\t\treadonly importable: boolean\n\t\treadonly requirable: boolean\n\t\treadonly browsable: boolean\n\t}\n\treadonly browsable: boolean\n\treadonly importable: boolean\n\treadonly requirable: boolean\n\treadonly loadable: boolean\n}\n\n// The installed tree every claim is read from. Every subpath the exports map names\n// lands in exactly one of `entries`, `undeclared`, and `excluded`, so a subpath this\n// proof cannot drive is reported rather than dropped.\ninterface Stage {\n\treadonly consumer: string\n\treadonly installed: string\n\treadonly archives: readonly string[]\n\treadonly entries: readonly Entry[]\n\treadonly subpaths: readonly string[]\n\treadonly undeclared: readonly string[]\n\treadonly excluded: readonly string[]\n\treadonly targets: readonly string[]\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isNames(value: unknown): value is readonly string[] {\n\treturn Array.isArray(value) && value.every((name) => typeof name === 'string')\n}\n\n// A fallback list, which is what Node reads an array in an exports entry as. The\n// narrowing is what the following walkers need: `Array.isArray` widens an `unknown`\n// member to `any`, and an entry read that way is not read at all.\nfunction isList(value: unknown): value is readonly unknown[] {\n\treturn Array.isArray(value)\n}\n\n// Whether a string is a valid package target. Node rejects a target outside the\n// package and a target containing a dot, parent, or node_modules segment during\n// package-target resolution. A later module-resolution failure is not the same\n// thing: an array falls through the former and keeps the latter.\nfunction isPackageTarget(target: string): boolean {\n\tif (!target.startsWith('./')) return false\n\tfor (const segment of target.slice(2).split(/[\\\\/]/u)) {\n\t\tlet decoded = segment\n\t\ttry {\n\t\t\tdecoded = decodeURIComponent(segment)\n\t\t} catch {}\n\t\tconst normalized = decoded.toLowerCase()\n\t\tif (normalized === '.' || normalized === '..' || normalized === 'node_modules') return false\n\t}\n\treturn true\n}\n\nfunction readJson(path: string): unknown {\n\tconst parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n\treturn parsed\n}\n\nfunction readManifestName(path: string): string {\n\tconst manifest = readJson(path)\n\tif (!isRecord(manifest) || typeof manifest.name !== 'string') {\n\t\tthrow new Error(`The manifest at ${path} declares no package name`)\n\t}\n\treturn manifest.name\n}\n\nfunction writeFile(path: string, content: string): void {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, content)\n}\n\nfunction readOutput(result: SpawnSyncReturns<string>): string {\n\treturn `${result.stdout ?? ''}${result.stderr ?? ''}`.trim()\n}\n\nfunction runNpm(args: readonly string[], cwd: string): SpawnSyncReturns<string> {\n\treturn spawnSync(NPM, [...args], {\n\t\tcwd,\n\t\tencoding: 'utf8',\n\t\tenv: { ...process.env, npm_config_cache: CACHE },\n\t\tshell: SHELL,\n\t\twindowsHide: true,\n\t})\n}\n\nfunction runNode(args: readonly string[], cwd: string): SpawnSyncReturns<string> {\n\treturn spawnSync(process.execPath, [...args], { cwd, encoding: 'utf8', windowsHide: true })\n}\n\n// Node's own condition matching, read in declaration order.\nfunction resolvePackageTarget(\n\tentry: unknown,\n\tconditions: readonly string[],\n): TargetResolution | undefined {\n\tif (typeof entry === 'string') return { target: entry }\n\tif (isList(entry)) {\n\t\tfor (const member of entry) {\n\t\t\tconst resolved = resolvePackageTarget(member, conditions)\n\t\t\tif (resolved !== undefined && isPackageTarget(resolved.target)) return resolved\n\t\t}\n\t\treturn undefined\n\t}\n\tif (!isRecord(entry)) return undefined\n\tfor (const [condition, nested] of Object.entries(entry)) {\n\t\tif (condition !== 'default' && !conditions.includes(condition)) continue\n\t\tconst resolved = resolvePackageTarget(nested, conditions)\n\t\tif (resolved !== undefined) return resolved\n\t}\n\treturn undefined\n}\n\n// A flat entry, a condition-nested entry, and a fallback list all resolve through\n// one walker. An entry may declare `types` beside `default` at its top level\n// rather than inside `import`, so a fixed `entry.import.types` lookup is not\n// equivalent to condition resolution.\nfunction resolveTarget(entry: unknown, conditions: readonly string[]): string | undefined {\n\treturn resolvePackageTarget(entry, conditions)?.target\n}\n\n// Whether a path is a physical file. TypeScript's file-existence check refuses a\n// directory at the same spelling and continues to the outer package scope.\nfunction matchesFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile()\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// TypeScript resolves a declaration target by accepting an existing declaration\n// directly or by substituting beside a JavaScript target. A missing target leaves\n// the containing condition or fallback list unresolved, so the walk continues.\nfunction targetToDeclaration(target: string, installed: string): string | undefined {\n\tif (!isPackageTarget(target)) return undefined\n\tlet declaration = target\n\tif (target.endsWith('.cjs')) declaration = `${target.slice(0, -4)}.d.cts`\n\telse if (target.endsWith('.mjs')) declaration = `${target.slice(0, -4)}.d.mts`\n\telse if (target.endsWith('.js')) declaration = `${target.slice(0, -3)}.d.ts`\n\telse if (!isDeclaration(target)) return undefined\n\treturn matchesFile(join(installed, declaration)) ? declaration : undefined\n}\n\n// The declaration TypeScript resolves through one importing format's conditions.\n// Condition objects keep manifest order, and arrays keep fallback order.\nfunction resolveDeclaration(\n\tentry: unknown,\n\tconditions: readonly string[],\n\tinstalled: string,\n): string | undefined {\n\tif (typeof entry === 'string') return targetToDeclaration(entry, installed)\n\tif (isList(entry)) {\n\t\tfor (const member of entry) {\n\t\t\tconst resolved = resolveDeclaration(member, conditions, installed)\n\t\t\tif (resolved !== undefined) return resolved\n\t\t}\n\t\treturn undefined\n\t}\n\tif (!isRecord(entry)) return undefined\n\tfor (const [condition, nested] of Object.entries(entry)) {\n\t\tif (condition !== 'default' && !conditions.includes(condition)) continue\n\t\tconst resolved = resolveDeclaration(nested, conditions, installed)\n\t\tif (resolved !== undefined) return resolved\n\t}\n\treturn undefined\n}\n\n// The nearest package scope that decides a `.d.ts` declaration's module format. A\n// physical nested manifest starts a scope even when it omits `type` or cannot be\n// parsed. A directory at that spelling is not a manifest, so the walk continues.\nfunction readPackageType(installed: string, target: string): unknown {\n\tlet directory = dirname(join(installed, target))\n\twhile (true) {\n\t\tconst path = join(directory, 'package.json')\n\t\tif (matchesFile(path)) {\n\t\t\ttry {\n\t\t\t\tconst manifest = readJson(path)\n\t\t\t\treturn isRecord(manifest) ? manifest.type : undefined\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\tif (directory === installed) return undefined\n\t\tconst parent = dirname(directory)\n\t\tif (parent === directory) return undefined\n\t\tdirectory = parent\n\t}\n}\n\nfunction resolvesBrowser(entry: unknown): boolean {\n\tconst module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)\n\tif (module !== undefined && module.startsWith(BROWSER_OUTPUT)) return true\n\tif (module === undefined) return false\n\tconst imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)\n\tconst required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\treturn module !== imported && module !== required\n}\n\n// Whether the target selected by Node's CommonJS conditions is a module require can\n// load. A JavaScript target takes its own nearest package scope. Native addons and\n// extensionless targets have their own CommonJS handlers.\nfunction resolvesCommonJS(entry: unknown, installed: string): boolean {\n\tconst target = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\tif (target === undefined) return false\n\tconst name = target.slice(target.lastIndexOf('/') + 1)\n\tif (name.endsWith('.cjs')) return true\n\tif (name.endsWith('.mjs')) return false\n\tif (name.endsWith('.node')) return true\n\tif (!name.includes('.')) return true\n\treturn name.endsWith('.js') && readPackageType(installed, target) !== 'module'\n}\n\n// Whether the declaration selected by a typed CommonJS consumer admits that entry.\n// A `.d.cts` declaration admits and a `.d.mts` declaration refuses. A `.d.ts`\n// declaration takes its own nearest package scope.\nfunction declaresCommonJS(entry: unknown, installed: string): boolean {\n\tconst declaration = resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed)\n\tif (declaration === undefined) return false\n\tif (declaration.endsWith('.d.cts')) return true\n\tif (declaration.endsWith('.d.mts')) return false\n\treturn declaration.endsWith('.d.ts') && readPackageType(installed, declaration) !== 'module'\n}\n\n// Every target an entry names under any condition. A fallback list omits members\n// Node rejects during package-target validation, because no reader can take them.\nfunction collectTargets(entry: unknown): readonly string[] {\n\tif (typeof entry === 'string') return [entry]\n\tif (isList(entry)) return entry.flatMap(collectTargets).filter(isPackageTarget)\n\tif (!isRecord(entry)) return []\n\treturn Object.values(entry).flatMap((nested) => collectTargets(nested))\n}\n\n// Whether a target is a file a runtime loads for its names, which is what a\n// declaration is owed for. The extension on the target's own file name decides it,\n// and a name carrying no extension is code: `require` reads such a file through its\n// JavaScript handler, so an extensionless target loads and publishes names. Node\n// loads `.node` through its native-addon handler. Every other extension is an asset\n// a consumer reads rather than imports — a stylesheet, a WebAssembly binary, the\n// `\"./package.json\"` manifest pointer, and a declaration alike.\n// The cost is an extensionless file published for a reader, such as a `LICENSE`:\n// that target reports undeclared until it is given an extension or a declaration.\nfunction isModule(target: string): boolean {\n\tconst name = target.slice(target.lastIndexOf('/') + 1)\n\tconst dot = name.lastIndexOf('.')\n\tif (name.endsWith(ADDON_EXTENSION)) return true\n\treturn dot === -1 || MODULE_EXTENSIONS.includes(name.slice(dot))\n}\n\n// Whether a resolved target is a declaration rather than the JavaScript a\n// `default` branch answers with when the entry declares no `types` condition.\nfunction isDeclaration(target: string): boolean {\n\treturn DECLARATION_EXTENSIONS.some((extension) => target.endsWith(extension))\n}\n\n// The declarations the Node module, Node CommonJS, and browser drives compare\n// against. Each field uses the conditions of the TypeScript consumer paired with\n// that runtime. A JavaScript target resolves through TypeScript's adjacent\n// declaration substitution rather than standing in for the declaration itself.\nfunction readDeclaration(\n\tentry: unknown,\n\tinstalled: string,\n): {\n\treadonly module: string | undefined\n\treadonly commonjs: string | undefined\n\treadonly browser: string | undefined\n} {\n\treturn {\n\t\tmodule: resolveDeclaration(entry, DECLARATION_CONDITIONS.module, installed),\n\t\tcommonjs: resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed),\n\t\tbrowser: resolveDeclaration(entry, DECLARATION_CONDITIONS.browser, installed),\n\t}\n}\n\n// The entries one compile driver can resolve under its own conditions.\nfunction selectEntries(entries: readonly Entry[], conditions: readonly string[]): readonly Entry[] {\n\treturn entries.filter(\n\t\t(entry) =>\n\t\t\tresolveTarget(entry.mapping, conditions) !== undefined &&\n\t\t\t(!conditions.includes('require') || entry.loadable),\n\t)\n}\n\n// The compile drivers whose conditions reach one entry under one importing format.\n// A driver that resolves no target for that entry compiles nothing, so a consumer\n// written under it would report a resolution failure this package never made.\nfunction selectDrivers(entry: Entry, format: Format): readonly Resolution[] {\n\treturn RESOLUTIONS.filter(\n\t\t(driver) => selectEntries([entry], driver.conditions[format]).length > 0,\n\t)\n}\n\n// Requirable entries that declare CommonJS support but a typed CommonJS consumer\n// cannot compile against. A default branch resolving under the require condition\n// set makes no CommonJS claim.\nfunction selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {\n\treturn entries.filter(\n\t\t(entry) =>\n\t\t\tentry.requirable &&\n\t\t\tisRecord(entry.mapping) &&\n\t\t\tObject.hasOwn(entry.mapping, 'require') &&\n\t\t\t!declaresCommonJS(entry.mapping, installed),\n\t)\n}\n\n// One surface comparison, written as the consumer module that proves it: the\n// installed entry that consumer imports, the file extension fixing its importing\n// format, the names a real runtime published off it, and the driver whose scratch\n// project compiles it.\ninterface Surface {\n\treadonly entry: Entry\n\treadonly extension: string\n\treadonly published: readonly string[]\n\treadonly driver: Resolution\n}\n\n// A scratch project over named consumer modules, written beside them so their own\n// resolution reaches the installed package. Nothing is emitted and no ambient types\n// are pulled in, so what the check reads is the installed declarations alone.\nfunction writeProject(\n\tstage: Stage,\n\tname: string,\n\tdriver: Resolution,\n\tfiles: readonly string[],\n): string {\n\tconst path = join(stage.consumer, `tsconfig.${name}.json`)\n\tconst project = {\n\t\tcompilerOptions: {\n\t\t\tmodule: driver.module,\n\t\t\tmoduleResolution: driver.resolution,\n\t\t\tnoEmit: true,\n\t\t\tskipLibCheck: true,\n\t\t\tstrict: true,\n\t\t\ttarget: 'esnext',\n\t\t\ttypes: [],\n\t\t},\n\t\tfiles: [...files],\n\t}\n\twriteFile(path, `${JSON.stringify(project, undefined, '\\t')}\\n`)\n\treturn path\n}\n\n// The diagnostics the compiler this workspace installs reports for one scratch\n// project. The compiler runs as a command, so nothing here reaches an API that\n// moves between its majors, and the located lines it prints are the verdict rather\n// than the exit code, which moves between them. A line carrying no location, a line\n// naming the scratch project rather than a consumer module, and anything at all on\n// the error stream are faults of this proof rather than of the package under proof,\n// so each is raised where it happens instead of counted against the package.\nfunction checkProject(stage: Stage, project: string): readonly string[] {\n\tconst result = runNode([TSC, '--noEmit', '--pretty', 'false', '-p', project], stage.consumer)\n\tconst refused = `${result.stderr ?? ''}`.trim()\n\tif (refused.length > 0) {\n\t\tthrow new Error(`The consumer compiler wrote ${refused} to its error stream`)\n\t}\n\tconst reported: string[] = []\n\tfor (const line of `${result.stdout ?? ''}`.split(/\\r\\n|\\n/u)) {\n\t\tif (line.trim().length === 0) continue\n\t\tconst last = reported.at(-1)\n\t\t// An elaborated diagnostic prints its detail on indented lines under its own\n\t\t// first line, so each of those joins the diagnostic it elaborates.\n\t\tif (/^\\s/u.test(line) && last !== undefined) {\n\t\t\treported[reported.length - 1] = `${last} ${line.trim()}`\n\t\t\tcontinue\n\t\t}\n\t\tconst located = DIAGNOSTIC_PATTERN.exec(line)?.[1]\n\t\tif (located === undefined) {\n\t\t\tthrow new Error(`The consumer compiler reported ${line}, which names no location`)\n\t\t}\n\t\tif (resolve(stage.consumer, located) === project) {\n\t\t\tthrow new Error(`The scratch project is itself at fault: ${line}`)\n\t\t}\n\t\treported.push(line)\n\t}\n\tif (reported.length === 0 && result.status !== 0) {\n\t\tthrow new Error(`The consumer compiler refused the project: ${readOutput(result)}`)\n\t}\n\treturn reported\n}\n\n// One installed entry's published names checked against its own declarations by\n// the compiler this workspace installs, in the direction each divergence surfaces\n// under. The runtime's key list is written into the consumer as a literal, so the\n// published side comes from a real process and the declared side from the\n// declarations that process's package ships, and the two can disagree. A name the\n// declarations carry and the runtime does not lands on `declared`. A name the\n// runtime carries and the declarations do not, and a name the declarations publish\n// as a type alone, land on `surfaced`: a variable widens into the type\n// `declared` annotates, and only `surfaced` reads the literal's own keys back.\n// Each names the member it is about, so the failure says which export moved.\nfunction checkSurface(stage: Stage, surface: Surface): readonly string[] {\n\tconst slug = surface.entry.subpath.replaceAll(/[^\\w]+/gu, '-')\n\tconst name = `surface.${surface.driver.label}${slug}.${surface.extension}`\n\tconst module = `${name}`\n\tconst keys = surface.published.map((key) => `${JSON.stringify(key)}: true`).join(', ')\n\twriteFile(\n\t\tjoin(stage.consumer, module),\n\t\t`import * as entry from ${JSON.stringify(surface.entry.specifier)}\nconst published = {${keys.length === 0 ? '' : ` ${keys} `}} as const\nconst declared: Record<keyof typeof entry, true> = published\nconst surfaced: Record<keyof typeof published, true> = declared\n`,\n\t)\n\tconst project = writeProject(stage, name, surface.driver, [`./${module}`])\n\treturn checkProject(stage, project).map((line) => `${surface.driver.label}: ${line}`)\n}\n\n// One consumer module importing every installed entry, written where its own\n// resolution finds the installed package.\nfunction writeConsumerProbe(stage: Stage, path: string, specifiers: readonly string[]): void {\n\tconst names: string[] = []\n\tconst bindings: string[] = []\n\tfor (const [index, specifier] of specifiers.entries()) {\n\t\tconst binding = `entry${String(index)}`\n\t\tnames.push(binding)\n\t\tbindings.push(`import * as ${binding} from ${JSON.stringify(specifier)}`)\n\t}\n\tconst source = `${bindings.join('\\n')}\\nexport const surface = [${names.join(', ')}]\\n`\n\twriteFile(join(stage.consumer, path), source)\n}\n\n// The runtime key set a real process reads off one installed entry under one\n// condition. The driver is a file rather than an `--eval` string, so the specifier\n// travels as an argument and nothing needs escaping.\nfunction driveRuntime(stage: Stage, specifier: string, driver: string): readonly string[] {\n\tconst result = runNode([join(stage.consumer, driver), specifier], stage.consumer)\n\tif (result.status !== 0) {\n\t\tthrow new Error(`Loading ${specifier} from the consumer failed: ${readOutput(result)}`)\n\t}\n\tconst published: unknown = JSON.parse(result.stdout)\n\tif (!isNames(published)) throw new Error(`The driver printed no name list for ${specifier}`)\n\treturn published\n}\n{{helpers}}\n// Pack this workspace, install the archive into an isolated consumer, and read the\n// published surface back off the installed tree. Every later claim reads this\n// result, so a failure here is raised where it happens rather than once per entry.\nfunction buildStage(): Stage {\n\tconst packed = join(SCRATCH, 'packed')\n\tconst consumer = join(SCRATCH, 'consumer')\n\tmkdirSync(packed, { recursive: true })\n\tconst pack = runNpm(['pack', '--ignore-scripts', '--pack-destination', packed], ROOT)\n\tif (pack.status !== 0) throw new Error(`npm pack refused this workspace: ${readOutput(pack)}`)\n\tconst archives = readdirSync(packed).filter((name) => name.endsWith('.tgz'))\n\tconst archive = archives[0]\n\tif (archives.length !== 1 || archive === undefined) {\n\t\tthrow new Error(`npm pack wrote no single archive: ${archives.join(', ')}`)\n\t}\n\twriteFile(join(consumer, 'package.json'), CONSUMER_MANIFEST)\n\twriteFile(join(consumer, ESM_DRIVER), ESM_DRIVER_SOURCE)\n\twriteFile(join(consumer, CJS_DRIVER), CJS_DRIVER_SOURCE)\n\tconst install = runNpm(\n\t\t['install', '--ignore-scripts', '--no-audit', '--no-fund', join(packed, archive)],\n\t\tconsumer,\n\t)\n\tif (install.status !== 0) {\n\t\tthrow new Error(`Installing the packed archive failed: ${readOutput(install)}`)\n\t}\n\tconst name = readManifestName(join(ROOT, 'package.json'))\n\tconst installed = join(consumer, 'node_modules', ...name.split('/'))\n\tconst manifest = readJson(join(installed, 'package.json'))\n\tif (!isRecord(manifest) || !isRecord(manifest.exports)) {\n\t\tthrow new Error('The installed manifest publishes no exports map')\n\t}\n\tconst entries: Entry[] = []\n\tconst targets: string[] = []\n\tconst subpaths: string[] = []\n\tconst undeclared: string[] = []\n\tconst excluded: string[] = []\n\tfor (const [subpath, entry] of Object.entries(manifest.exports)) {\n\t\tconst files = collectTargets(entry)\n\t\ttargets.push(...files)\n\t\tsubpaths.push(subpath)\n\t\tconst declaration = readDeclaration(entry, installed)\n\t\t// A subpath resolving no declaration is partitioned rather than dropped. It is a\n\t\t// defect when a runtime loads one of its targets for names, because a consumer\n\t\t// importing it compiles against nothing under `node16`. It is an excluded\n\t\t// publication otherwise: the `\"./package.json\"` manifest pointer and a stylesheet\n\t\t// are published for a reader rather than an importer.\n\t\tif (\n\t\t\tdeclaration.module === undefined &&\n\t\t\tdeclaration.commonjs === undefined &&\n\t\t\tdeclaration.browser === undefined\n\t\t) {\n\t\t\tif (files.some(isModule)) undeclared.push(subpath)\n\t\t\telse excluded.push(subpath)\n\t\t\tcontinue\n\t\t}\n\t\tconst importTarget = resolveTarget(entry, RUNTIME_CONDITIONS.module)\n\t\tconst requireTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)\n\t\tconst browserTarget = resolveTarget(entry, RUNTIME_CONDITIONS.browser)\n\t\tconst browsable = resolvesBrowser(entry)\n\t\tconst shadowed = browsable && requireTarget === browserTarget\n\t\tconst requirable = requireTarget !== undefined && !shadowed\n\t\tconst loadable = requirable && resolvesCommonJS(entry, installed)\n\t\tentries.push({\n\t\t\tsubpath,\n\t\t\tspecifier: subpath === '.' ? name : `${name}${subpath.slice(1)}`,\n\t\t\tmapping: entry,\n\t\t\tdeclaration: {\n\t\t\t\timportable: declaration.module !== undefined,\n\t\t\t\trequirable: declaration.commonjs !== undefined,\n\t\t\t\tbrowsable: declaration.browser !== undefined,\n\t\t\t},\n\t\t\tbrowsable,\n\t\t\timportable: importTarget !== undefined && !(browsable && importTarget === browserTarget),\n\t\t\trequirable,\n\t\t\tloadable,\n\t\t})\n\t}\n\treturn { consumer, installed, archives, entries, subpaths, undeclared, excluded, targets }\n}\n\nconst SCRATCH = mkdtempSync(join(tmpdir(), 'distribution-'))\nconst CACHE = join(SCRATCH, 'cache')\nmkdirSync(CACHE, { recursive: true })\n// The scratch tree holds the npm cache, the packed archive, and the installed\n// consumer, so its removal is registered before the first thing that can throw.\nafterAll(() => {\n\trmSync(SCRATCH, { force: true, recursive: true })\n})\n\n// Installing the packed archive resolves its own runtime dependencies, so an\n// unreachable registry leaves nothing to measure. Under release that is the gate\n// failing; anywhere else the suite skips and names the mechanism it wanted.\n//\n// A module that throws while loading never reaches the `afterAll` it registered,\n// so every throw here removes the scratch tree on its way out.\nfunction openStage(): Stage | undefined {\n\ttry {\n\t\tif (runNpm(PING, ROOT).status !== 0) {\n\t\t\tif (!RELEASE) return undefined\n\t\t\tthrow new Error(\n\t\t\t\t'The release gate requires a reachable npm registry, and npm ping did not answer',\n\t\t\t)\n\t\t}\n\t\treturn buildStage()\n\t} catch (error) {\n\t\trmSync(SCRATCH, { force: true, recursive: true })\n\t\tthrow error\n\t}\n}\n\nconst STAGE = openStage()\nconst STAGED = STAGE !== undefined\n\ndescribe('distribution classifiers', () => {\n\tit('classifies synthetic export mappings without a registry stage', () => {\n\t\tconst root = join(SCRATCH, 'classifiers')\n\t\twriteFile(\n\t\t\tjoin(root, 'package.json'),\n\t\t\tJSON.stringify({\n\t\t\t\ttype: 'commonjs',\n\t\t\t\texports: {\n\t\t\t\t\tcondition: { browser: './b.js', default: './n.js' },\n\t\t\t\t\tconvention: { default: './dist/src/browser/index.js' },\n\t\t\t\t\tuniversal: { default: './shared.js' },\n\t\t\t\t\t'import-shared': {\n\t\t\t\t\t\tbrowser: './shared.mjs',\n\t\t\t\t\t\timport: './shared.mjs',\n\t\t\t\t\t\tdefault: './node.js',\n\t\t\t\t\t},\n\t\t\t\t\t'require-shared': {\n\t\t\t\t\t\tbrowser: './shared.cjs',\n\t\t\t\t\t\trequire: './shared.cjs',\n\t\t\t\t\t\tdefault: './node.js',\n\t\t\t\t\t},\n\t\t\t\t\tnode: { node: './node.js', default: './node.js' },\n\t\t\t\t\tsilent: { 'module-sync': './x.cjs', import: './x.mjs' },\n\t\t\t\t\tmodule: { require: './x.mjs' },\n\t\t\t\t\t'nested-module': { require: './module/x.js' },\n\t\t\t\t\t'nested-commonjs': { require: './commonjs/x.js' },\n\t\t\t\t\tesm: { import: './x.mjs' },\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\t\twriteFile(join(root, 'module/package.json'), '{ \"type\": \"module\" }\\n')\n\t\twriteFile(join(root, 'commonjs/package.json'), '{ \"type\": \"commonjs\" }\\n')\n\t\tconst manifest = readJson(join(root, 'package.json'))\n\t\tif (!isRecord(manifest) || !isRecord(manifest.exports)) {\n\t\t\tthrow new Error('The classifier fixture declares no exports map')\n\t\t}\n\t\tconst mappings = manifest.exports\n\t\texpect({\n\t\t\tcondition: resolvesBrowser(mappings.condition),\n\t\t\tconvention: resolvesBrowser(mappings.convention),\n\t\t\tuniversal: resolvesBrowser(mappings.universal),\n\t\t\timport: resolvesBrowser(mappings['import-shared']),\n\t\t\trequire: resolvesBrowser(mappings['require-shared']),\n\t\t\tnode: resolvesBrowser(mappings.node),\n\t\t}).toStrictEqual({\n\t\t\tcondition: true,\n\t\t\tconvention: true,\n\t\t\tuniversal: false,\n\t\t\timport: false,\n\t\t\trequire: false,\n\t\t\tnode: false,\n\t\t})\n\t\texpect({\n\t\t\tsilent: resolvesCommonJS(mappings.silent, root),\n\t\t\tmodule: resolvesCommonJS(mappings.module, root),\n\t\t\tnestedModule: resolvesCommonJS(mappings['nested-module'], root),\n\t\t\tnestedCommonJS: resolvesCommonJS(mappings['nested-commonjs'], root),\n\t\t\tesm: resolvesCommonJS(mappings.esm, root),\n\t\t}).toStrictEqual({\n\t\t\tsilent: true,\n\t\t\tmodule: false,\n\t\t\tnestedModule: false,\n\t\t\tnestedCommonJS: true,\n\t\t\tesm: false,\n\t\t})\n\t})\n})\n\n// The staged consumer, or a skip naming what the run could not reach. `it.skipIf`\n// carries no reason, so the gate sits here where the test context can state one.\nfunction requireStage(context: TestContext): Stage {\n\tif (!STAGED) {\n\t\treturn context.skip('`npm ping` did not answer, so nothing was packed or installed')\n\t}\n\treturn STAGE\n}\n\ndescribe('installed package consumer', () => {\n\tit('packs one archive and installs it in isolation [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\texpect(stage.archives).toHaveLength(1)\n\t\texpect(existsSync(join(stage.installed, 'package.json'))).toBe(true)\n\t\texpect(stage.entries.length).toBeGreaterThan(0)\n\t})\n\n\tit('ships every relative target its exports map names [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst relative = stage.targets.filter((target) => target.startsWith('./'))\n\t\texpect(relative).not.toStrictEqual([])\n\t\texpect(relative.filter((target) => !existsSync(join(stage.installed, target)))).toStrictEqual(\n\t\t\t[],\n\t\t)\n\t})\n\n\t// Every published subpath is driven, excluded by name, or reported here. A dropped\n\t// one leaves no trace: no runtime test, no declaration comparison, and no place in\n\t// the resolution compile, so the run reports success for a subpath it never\n\t// measured.\n\tit('declares types for every module it publishes [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst partitioned = [\n\t\t\t...stage.entries.map((entry) => entry.subpath),\n\t\t\t...stage.undeclared,\n\t\t\t...stage.excluded,\n\t\t]\n\t\texpect(stage.undeclared).toStrictEqual([])\n\t\texpect(partitioned.sort()).toStrictEqual([...stage.subpaths].sort())\n\t\t// A driven subpath answers a runtime condition. One resolving a declaration and\n\t\t// no Node or browser target compiles for a consumer and throws when that consumer\n\t\t// loads it. Each later drive retires itself for that entry, so this assertion names\n\t\t// the subpath rather than counting it as driven.\n\t\tconst unreachable = stage.entries.filter(\n\t\t\t(entry) => !entry.importable && !entry.requirable && !entry.browsable,\n\t\t)\n\t\texpect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])\n\t\tconst untypable = selectUntypable(stage.entries, stage.installed)\n\t\texpect(untypable.map((entry) => entry.subpath)).toStrictEqual([])\n\t})\n\n\tit('refuses a subpath its exports map does not name [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst name = readManifestName(join(stage.installed, 'package.json'))\n\t\tconst driver = join(stage.consumer, ESM_DRIVER)\n\t\tconst result = runNode([driver, `${name}${ABSENT_SUBPATH}`], stage.consumer)\n\t\texpect(result.status).not.toBe(0)\n\t\texpect(readOutput(result)).toContain('ERR_PACKAGE_PATH_NOT_EXPORTED')\n\t})\n\n\t// The absent subpath is the firing control: a resolution that reports nothing\n\t// for every published entry has not been shown to resolve anything at all. Each\n\t// module format carries its own control, because a format that resolves nothing\n\t// is silent for the same reason a resolution that resolves nothing is.\n\tit('compiles a consumer under every module resolution [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst name = readManifestName(join(stage.installed, 'package.json'))\n\t\tconst reported: string[] = []\n\t\tconst silent: string[] = []\n\t\tfor (const driver of RESOLUTIONS) {\n\t\t\tfor (const [extension, format] of FORMATS) {\n\t\t\t\tconst written = selectEntries(stage.entries, driver.conditions[format])\n\t\t\t\tif (written.length === 0) continue\n\t\t\t\tconst label = `${driver.label}.${extension}`\n\t\t\t\tconst probe = `probe.${label}`\n\t\t\t\tconst specifiers = written.map((entry) => entry.specifier)\n\t\t\t\twriteConsumerProbe(stage, probe, specifiers)\n\t\t\t\tconst project = writeProject(stage, probe, driver, [`./${probe}`])\n\t\t\t\tfor (const message of checkProject(stage, project)) {\n\t\t\t\t\treported.push(`${label}: ${message}`)\n\t\t\t\t}\n\t\t\t\tconst control = `control.${label}`\n\t\t\t\twriteConsumerProbe(stage, control, [`${name}${ABSENT_SUBPATH}`])\n\t\t\t\tconst refused = writeProject(stage, control, driver, [`./${control}`])\n\t\t\t\tif (checkProject(stage, refused).length === 0) silent.push(label)\n\t\t\t}\n\t\t}\n\t\texpect(reported).toStrictEqual([])\n\t\texpect(silent).toStrictEqual([])\n\t})\n{{guard}}})\n\nfor (const entry of STAGE?.entries ?? []) {\n\tdescribe(`installed entry ${entry.subpath}`, () => {\n\t\tit.runIf(entry.importable)(\n\t\t\t'publishes what it declares to a Node import, and no more',\n\t\t\t(context) => {\n\t\t\t\tconst stage = requireStage(context)\n\t\t\t\t// The exports-map walk resolved a declaration a typed importer reads, so an\n\t\t\t\t// entry reaching this drive without one is reported for that rather than for\n\t\t\t\t// what a consumer of a missing declaration goes on to say.\n\t\t\t\tif (!entry.declaration.importable) {\n\t\t\t\t\tthrow new Error(`${entry.subpath} publishes no import declaration`)\n\t\t\t\t}\n\t\t\t\tconst published = driveRuntime(stage, entry.specifier, ESM_DRIVER)\n\t\t\t\tconst drivers = selectDrivers(entry, 'module')\n\t\t\t\texpect(drivers).not.toStrictEqual([])\n\t\t\t\tconst reported = drivers.flatMap((driver) =>\n\t\t\t\t\tcheckSurface(stage, { entry, extension: 'ts', published, driver }),\n\t\t\t\t)\n\t\t\t\texpect(reported).toStrictEqual([])\n\t\t\t},\n\t\t)\n\n\t\tit.runIf(entry.requirable)(\n\t\t\t'publishes what it declares to a Node require, and no more',\n\t\t\t(context) => {\n\t\t\t\tconst stage = requireStage(context)\n\t\t\t\tif (!entry.declaration.requirable) {\n\t\t\t\t\tthrow new Error(`${entry.subpath} publishes no require declaration`)\n\t\t\t\t}\n\t\t\t\tconst published = driveRuntime(stage, entry.specifier, CJS_DRIVER)\n\t\t\t\t// A subpath whose `require` resolves to a module that no typed CommonJS\n\t\t\t\t// consumer can compile against carries no declared side to compare here, and\n\t\t\t\t// whether it may publish one at all is the untypable set's question rather\n\t\t\t\t// than this drive's. The preceding runtime drive ran either way.\n\t\t\t\tconst drivers = selectDrivers(entry, 'commonjs')\n\t\t\t\texpect(drivers).not.toStrictEqual([])\n\t\t\t\tconst reported = drivers.flatMap((driver) =>\n\t\t\t\t\tcheckSurface(stage, { entry, extension: 'cts', published, driver }),\n\t\t\t\t)\n\t\t\t\texpect(reported).toStrictEqual([])\n\t\t\t},\n\t\t)\n{{drive}}\t})\n}\n";
|
|
105
105
|
transport: "import { createServer } from 'node:http'\n";
|
|
106
106
|
types: "import type { PlaywrightProviderOptions } from '@vitest/browser-playwright'\nimport type { Browser } from 'playwright'\n";
|
|
107
107
|
launcher: "import { chromium } from 'playwright'\nimport { build } from 'vite'\nimport { resolveBrowser, resolvePinnedBrowser } from '../configs/browsers.js'\n";
|
|
108
108
|
helpers: "\nconst BROWSER_PAGE = `<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<title>Distribution</title>\n\t</head>\n\t<body>\n\t\t<script type=\"module\" src=\"./main.js\"></script>\n\t</body>\n</html>\n`\n\nfunction readContentType(path: string): string {\n\tif (path.endsWith('.html')) return 'text/html'\n\tif (path.endsWith('.js')) return 'text/javascript'\n\tif (path.endsWith('.css')) return 'text/css'\n\tif (path.endsWith('.json') || path.endsWith('.map')) return 'application/json'\n\treturn 'application/octet-stream'\n}\n\n// `resolveBrowser` answers with provider options and never reports absence: its\n// last resort is a channel nothing verified. So the launch is attempted and its\n// rejection classified, rather than probed for and ruled on.\nfunction describeBrowser(options: PlaywrightProviderOptions): string {\n\tconst endpoint = options.connectOptions?.wsEndpoint\n\tif (endpoint !== undefined) return `the browser server at ${endpoint}`\n\tconst executable = options.launchOptions?.executablePath\n\tif (executable !== undefined) return `the executable at ${executable}`\n\tconst channel = options.launchOptions?.channel\n\tif (channel !== undefined) return `the ${channel} channel`\n\treturn 'the Chromium Playwright installed for itself'\n}\n\nasync function launchBrowser(options: PlaywrightProviderOptions): Promise<Browser> {\n\tconst endpoint = options.connectOptions?.wsEndpoint\n\tif (endpoint !== undefined) return chromium.connect(endpoint)\n\treturn chromium.launch({ ...options.launchOptions, headless: true })\n}\n\n// A consumer of one installed browser entry, bundled by the Vite toolchain this\n// workspace already declares. Nothing is stubbed: the bundle resolves the installed\n// package and its whole transitive graph as an application consuming it would.\nasync function bundleEntry(stage: Stage, entry: Entry): Promise<string> {\n\tconst page = join(stage.consumer, 'pages', entry.subpath.replaceAll(/[^\\w]+/gu, '-'))\n\tconst specifier = JSON.stringify(entry.specifier)\n\twriteFile(join(page, 'index.html'), BROWSER_PAGE)\n\twriteFile(\n\t\tjoin(page, 'main.js'),\n\t\t`import * as entry from ${specifier}\\nglobalThis.subject = Object.keys(entry).sort()\\n`,\n\t)\n\tawait build({\n\t\tbase: './',\n\t\tbuild: { emptyOutDir: true, outDir: 'bundle' },\n\t\tconfigFile: false,\n\t\tlogLevel: 'error',\n\t\troot: page,\n\t})\n\treturn join(page, 'bundle')\n}\n\n// The key set the bundled module publishes in a real browser, read off the page\n// once it has loaded over a loopback server. A module that never evaluated\n// publishes nothing, and a page error is raised rather than compared away.\nasync function readBrowserExports(browser: Browser, bundle: string): Promise<readonly string[]> {\n\tconst server = createServer((request, response) => {\n\t\tconst asked = request.url === undefined || request.url === '/' ? '/index.html' : request.url\n\t\tconst path = join(bundle, decodeURIComponent(asked))\n\t\tif (!path.startsWith(bundle) || !existsSync(path)) {\n\t\t\tresponse.writeHead(404)\n\t\t\tresponse.end()\n\t\t\treturn\n\t\t}\n\t\tresponse.writeHead(200, { 'content-type': readContentType(path) })\n\t\tresponse.end(readFileSync(path))\n\t})\n\ttry {\n\t\tawait new Promise<void>((settle) => {\n\t\t\tserver.listen(0, '127.0.0.1', settle)\n\t\t})\n\t\tconst address = server.address()\n\t\tif (address === null || typeof address === 'string') {\n\t\t\tthrow new Error('The bundle server bound no port')\n\t\t}\n\t\tconst page = await browser.newPage()\n\t\tconst failures: string[] = []\n\t\tpage.on('pageerror', (error) => failures.push(String(error)))\n\t\tawait page.goto(`http://127.0.0.1:${String(address.port)}/`, { waitUntil: 'load' })\n\t\tconst published: unknown = await page.evaluate('globalThis.subject')\n\t\tif (failures.length > 0) throw new Error(`The bundle raised ${failures.join(' | ')}`)\n\t\tif (!isNames(published)) throw new Error('The bundled module published no name list')\n\t\treturn published\n\t} finally {\n\t\tserver.close()\n\t}\n}\n";
|
|
109
|
-
drive: "\n\t\tit.runIf(entry.
|
|
110
|
-
guard: "\n\t// This proof drives a Node import and a Node require and carries no browser\n\t// branch: the workspace published no browser face when it was written, and the\n\t// browser drive measures the packed artifact, so only a published face is owed\n\t// one. A private browser application does not select this branch. It declares the\n\t// browser launcher and its Vitest browser provider and gets the generated browser\n\t// configuration module beside it, but installed browser tooling does not stand for\n\t// a published browser face. `vite` selects nothing either, though the branch\n\t// imports it: scaffold puts `vite` in every workspace's base development\n\t// dependencies, whatever that workspace publishes. The later Node\n\t// `it.runIf` predicates retire each matching Node drive for a face published\n\t// later, which leaves nothing measuring it. So it reddens here and names the\n\t// subpath a browser branch is owed for. A workspace that gains one deletes this\n\t// file and runs the `repair` verb, which writes the variant carrying that branch.\n\tit('publishes no browser face this proof cannot drive [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst faces = stage.entries.filter((entry) => entry.
|
|
109
|
+
drive: "\n\t\tit.runIf(entry.browsable)(\n\t\t\t'publishes what it declares to a real browser, and no more [requires a browser]',\n\t\t\tasync (context) => {\n\t\t\t\tconst stage = requireStage(context)\n\t\t\t\tif (!entry.declaration.browsable) {\n\t\t\t\t\tthrow new Error(`${entry.subpath} publishes no browser declaration`)\n\t\t\t\t}\n\t\t\t\tconst options = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n\t\t\t\tconst browser = await launchBrowser(options).catch((error: unknown) => {\n\t\t\t\t\tconst cause = `${describeBrowser(options)} was rejected: ${String(error)}`\n\t\t\t\t\tif (RELEASE) throw new Error(`The release gate requires a browser, and ${cause}`)\n\t\t\t\t\treturn context.skip(`No browser launched. ${cause}`)\n\t\t\t\t})\n\t\t\t\ttry {\n\t\t\t\t\tconst bundle = await bundleEntry(stage, entry)\n\t\t\t\t\tconst published = await readBrowserExports(browser, bundle)\n\t\t\t\t\t// A browser consumer reads the installed declarations through a bundler, so\n\t\t\t\t\t// that is the one driver this face answers under.\n\t\t\t\t\tconst reported = checkSurface(stage, {\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\textension: 'ts',\n\t\t\t\t\t\tpublished,\n\t\t\t\t\t\tdriver: BROWSER_DRIVER,\n\t\t\t\t\t})\n\t\t\t\t\texpect(reported).toStrictEqual([])\n\t\t\t\t} finally {\n\t\t\t\t\tawait browser.close()\n\t\t\t\t}\n\t\t\t},\n\t\t)\n";
|
|
110
|
+
guard: "\n\t// This proof drives a Node import and a Node require and carries no browser\n\t// branch: the workspace published no browser face when it was written, and the\n\t// browser drive measures the packed artifact, so only a published face is owed\n\t// one. A private browser application does not select this branch. It declares the\n\t// browser launcher and its Vitest browser provider and gets the generated browser\n\t// configuration module beside it, but installed browser tooling does not stand for\n\t// a published browser face. `vite` selects nothing either, though the branch\n\t// imports it: scaffold puts `vite` in every workspace's base development\n\t// dependencies, whatever that workspace publishes. The later Node\n\t// `it.runIf` predicates retire each matching Node drive for a face published\n\t// later, which leaves nothing measuring it. So it reddens here and names the\n\t// subpath a browser branch is owed for. A workspace that gains one deletes this\n\t// file and runs the `repair` verb, which writes the variant carrying that branch.\n\tit('publishes no browser face this proof cannot drive [requires the registry]', (context) => {\n\t\tconst stage = requireStage(context)\n\t\tconst faces = stage.entries.filter((entry) => entry.browsable)\n\t\texpect(faces.map((entry) => entry.subpath)).toStrictEqual([])\n\t})\n";
|
|
111
111
|
}>;
|
|
112
112
|
integration: "{{imports}}import { describe, expect, it } from 'vitest'\n\ndescribe('workspace integration', () => {\n\tit('loads every selected public barrel together as a composition seed', () => {\n\t\t// Replace this empty-barrel seed with one observable flow that passes one\n\t\t// environment's public result into another.\n\t\t{{actual}}{{expected}})\n\t})\n})\n";
|
|
113
113
|
}>;
|
|
@@ -395,6 +395,46 @@ export declare function blueprintToDocumentArtifacts(blueprint: Blueprint): read
|
|
|
395
395
|
*/
|
|
396
396
|
export declare function blueprintToGuideArtifacts(blueprint: Blueprint): readonly ContentArtifact[];
|
|
397
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Compiles the vendored host artifacts a workspace plans.
|
|
400
|
+
*
|
|
401
|
+
* @param blueprint - The workspace specification.
|
|
402
|
+
* @returns One artifact per selected vendored path in `HOST_PATHS` order, then
|
|
403
|
+
* the catalog file.
|
|
404
|
+
*
|
|
405
|
+
* @remarks
|
|
406
|
+
* Every artifact is claimed by presence, which is the strongest claim a pure
|
|
407
|
+
* compile can make: core cannot read the vendored data root, so it cannot carry
|
|
408
|
+
* the bytes a content claim would have to be checked against. Reading that root
|
|
409
|
+
* is what promotes the ones scaffold owns the bytes of.
|
|
410
|
+
*
|
|
411
|
+
* `source` is left absent because it falls back to `path`, and every vendored
|
|
412
|
+
* path is stored under the name it is written to. The group comes from
|
|
413
|
+
* {@link inferGroup}, so a vendored path and a foreign path found in a target
|
|
414
|
+
* are classified by one rule and a plan never disagrees with the audit beside
|
|
415
|
+
* it.
|
|
416
|
+
*
|
|
417
|
+
* {@link CATALOG_AGENT_PATH} is appended rather than listed, and it is the canon
|
|
418
|
+
* path this compiler claims. The catalog verb refuses a target that lacks the
|
|
419
|
+
* file, so the plan has to carry it; repair restores its absence from the
|
|
420
|
+
* staged bytes; and the `.claude/agents` directory in `CANON_PATHS` is what
|
|
421
|
+
* stages those bytes, so listing the file in `HOST_PATHS` as well would claim one
|
|
422
|
+
* storage name twice and refuse the stage. The rest of the canon a target reads
|
|
423
|
+
* from the installed package, at the locations the `AGENTS.md` and `CLAUDE.md`
|
|
424
|
+
* pointers {@link blueprintToDocumentArtifacts} emits name.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* import { blueprintToHostArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
429
|
+
*
|
|
430
|
+
* const blueprint = createBlueprint('router')
|
|
431
|
+
*
|
|
432
|
+
* blueprintToHostArtifacts(blueprint).some((artifact) => artifact.path === 'tests/guides.test.ts') // false
|
|
433
|
+
* blueprintToHostArtifacts(blueprint).some((artifact) => artifact.path === 'guides/router.md') // false
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
export declare function blueprintToHostArtifacts(blueprint: Blueprint): readonly Artifact[];
|
|
437
|
+
|
|
398
438
|
/**
|
|
399
439
|
* Derives the host-specific machinery a generated root Vite configuration carries.
|
|
400
440
|
*
|
|
@@ -537,6 +577,21 @@ export declare function blueprintToQuestions(blueprint: Blueprint): readonly Que
|
|
|
537
577
|
* @param blueprint - The workspace specification.
|
|
538
578
|
* @returns Formatter-stable `tsconfig.json` text.
|
|
539
579
|
*
|
|
580
|
+
* @remarks
|
|
581
|
+
* `paths` carries the workspace's own published specifiers beside the `@src` and
|
|
582
|
+
* `@app` aliases, mapped to source. The published set is what
|
|
583
|
+
* {@link srcToExports} publishes, read the same way here so the two never name
|
|
584
|
+
* different subpaths, and the `app` axis publishes nothing so it maps nothing.
|
|
585
|
+
* A package's own name resolves inside its own checkout through that `exports`
|
|
586
|
+
* map, to the built `dist/` entry, so a module importing it would make the root
|
|
587
|
+
* typecheck depend on a build; mapping the specifier to source is what keeps
|
|
588
|
+
* `npm run check` independent of `npm run build`. The root Vite configuration
|
|
589
|
+
* derives its `alias` record from every `paths` entry, so the same specifiers
|
|
590
|
+
* resolve to source under Vitest, and every subpath is emitted before the bare
|
|
591
|
+
* specifier: an alias record is matched in declaration order and a bare pattern
|
|
592
|
+
* also matches its own subpaths, so the reverse order would resolve
|
|
593
|
+
* `@orkestrel/<name>/server` through the core entry.
|
|
594
|
+
*
|
|
540
595
|
* @example
|
|
541
596
|
* ```ts
|
|
542
597
|
* import { blueprintToRootTsconfig, createBlueprint } from '@orkestrel/scaffold'
|
|
@@ -678,13 +733,16 @@ export declare function blueprintToTestArtifacts(blueprint: Blueprint): readonly
|
|
|
678
733
|
*
|
|
679
734
|
* @remarks
|
|
680
735
|
* Every direct `test:<project>` script is writable, together with the probe and
|
|
681
|
-
* benchmark workbench scripts.
|
|
682
|
-
*
|
|
683
|
-
*
|
|
736
|
+
* benchmark workbench scripts. A workspace carrying guides adds `test:guides`,
|
|
737
|
+
* whose package-owned entry runs the guides project and handles explicit parity
|
|
738
|
+
* rewrites. Publishing adds the pack and publication lifecycle scripts.
|
|
739
|
+
* Aggregate test scripts and
|
|
740
|
+
* maintainer-owned gate chains stay outside the region.
|
|
684
741
|
*
|
|
685
742
|
* `accepted` carries each generated predecessor the region can replace. The
|
|
686
|
-
*
|
|
687
|
-
*
|
|
743
|
+
* guides entry accepts the generated command that invoked the guides project
|
|
744
|
+
* through Vitest alone. The pack hook accepts the build chain emitted before it
|
|
745
|
+
* delegated to `build`. The publication hook accepts the same gate chain without
|
|
688
746
|
* {@link RELEASE_PROOF_COMMAND}. The value being written is always writable, so
|
|
689
747
|
* it is not repeated there. Any other value is a script the workspace author
|
|
690
748
|
* customized, and {@link replaceManifestScripts} retains it while writing the
|
|
@@ -748,7 +806,7 @@ export declare function bytesToHex(bytes: Uint8Array): string;
|
|
|
748
806
|
*
|
|
749
807
|
* The plan claims paths inside the canon deliberately, and each has a reason.
|
|
750
808
|
* `blueprintToDocumentArtifacts` claims `AGENTS.md` and `CLAUDE.md` as this
|
|
751
|
-
* package's own template pointers. `
|
|
809
|
+
* package's own template pointers. `blueprintToHostArtifacts` claims
|
|
752
810
|
* {@link CATALOG_AGENT_PATH}, because the catalog verb refuses a target that
|
|
753
811
|
* lacks the file and repair restores its absence.
|
|
754
812
|
*
|
|
@@ -763,7 +821,7 @@ export declare const CANON_PATHS: readonly string[];
|
|
|
763
821
|
*
|
|
764
822
|
* @remarks
|
|
765
823
|
* A plan claims it at a canon path, because the catalog verb refuses a target
|
|
766
|
-
* that lacks the file. `
|
|
824
|
+
* that lacks the file. `blueprintToHostArtifacts` appends it to the vendored
|
|
767
825
|
* selection, and it reaches a release through the `.claude/agents` directory in
|
|
768
826
|
* {@link CANON_PATHS} rather than through {@link HOST_PATHS}, which is what
|
|
769
827
|
* keeps the two lists disjoint.
|
|
@@ -806,16 +864,20 @@ export declare const CATALOG_OPENING_MARKER = "<!-- orkestrel:catalog -->";
|
|
|
806
864
|
* `dependencies` are the RUNTIME edges the published version declares, which is
|
|
807
865
|
* what a publish order is computed over: a runtime bump obliges every dependent
|
|
808
866
|
* to re-pin and republish, while a development bump obliges nothing beyond the
|
|
809
|
-
* repository that declares it.
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
867
|
+
* repository that declares it. `peers` are the edges the published version
|
|
868
|
+
* declares under `peerDependencies`. A peer edge orders a dependent the same
|
|
869
|
+
* way a runtime edge does: at `0.0.x` a caret peer range pins one exact
|
|
870
|
+
* release, so the peer must be on the registry before the dependent publishes.
|
|
871
|
+
* No layer is recorded here, because a layer is a deterministic function of
|
|
872
|
+
* these edges across the whole catalog — a stored one could only disagree with
|
|
873
|
+
* the rows it was derived from. Read it through {@link catalogToLayers}.
|
|
813
874
|
*/
|
|
814
875
|
export declare type CatalogEntry = {
|
|
815
876
|
readonly name: string;
|
|
816
877
|
readonly lookup: 'found';
|
|
817
878
|
readonly version: string;
|
|
818
879
|
readonly dependencies: readonly Dependency[];
|
|
880
|
+
readonly peers: readonly Dependency[];
|
|
819
881
|
readonly note?: never;
|
|
820
882
|
} | {
|
|
821
883
|
readonly name: string;
|
|
@@ -823,6 +885,7 @@ export declare type CatalogEntry = {
|
|
|
823
885
|
readonly note: string;
|
|
824
886
|
readonly version?: never;
|
|
825
887
|
readonly dependencies?: never;
|
|
888
|
+
readonly peers?: never;
|
|
826
889
|
};
|
|
827
890
|
|
|
828
891
|
/**
|
|
@@ -835,9 +898,11 @@ export declare type CatalogEntry = {
|
|
|
835
898
|
* @remarks
|
|
836
899
|
* A layer is a deterministic function of the catalog's own edges, so it is
|
|
837
900
|
* computed here rather than stored on a row that could disagree with them.
|
|
838
|
-
* Only RUNTIME edges between catalogued packages count: a development
|
|
901
|
+
* Only RUNTIME and PEER edges between catalogued packages count: a development
|
|
839
902
|
* dependency reaches no consumer, so it constrains nothing about publish order,
|
|
840
|
-
* and an edge leaving the fleet is a package this catalog does not publish.
|
|
903
|
+
* and an edge leaving the fleet is a package this catalog does not publish. A
|
|
904
|
+
* peer edge counts like a runtime edge, because a caret peer range at `0.0.x`
|
|
905
|
+
* names one exact release the dependent cannot publish ahead of.
|
|
841
906
|
*
|
|
842
907
|
* The order matters because these packages are `0.0.x`, where a caret pins one
|
|
843
908
|
* exact release. Publishing a dependent before its dependency leaves the
|
|
@@ -848,11 +913,12 @@ export declare type CatalogEntry = {
|
|
|
848
913
|
* placed in an order that would be wrong. An absent name is the report: compare
|
|
849
914
|
* the returned names against the catalog to find one.
|
|
850
915
|
*
|
|
851
|
-
* @example
|
|
916
|
+
* @example Fleet catalog
|
|
852
917
|
* ```ts
|
|
853
918
|
* import { catalogToLayers } from '@orkestrel/scaffold'
|
|
854
919
|
*
|
|
855
|
-
* catalogToLayers(entries)
|
|
920
|
+
* const layers = catalogToLayers(entries)
|
|
921
|
+
* layers[0] // the names that depend on nothing else in the fleet
|
|
856
922
|
* ```
|
|
857
923
|
*/
|
|
858
924
|
export declare function catalogToLayers(entries: readonly CatalogEntry[]): ReadonlyArray<readonly string[]>;
|
|
@@ -963,11 +1029,13 @@ export declare interface CompileFailure {
|
|
|
963
1029
|
*
|
|
964
1030
|
* @example
|
|
965
1031
|
* ```ts
|
|
966
|
-
* import {
|
|
1032
|
+
* import { Compiler, createBlueprint } from '@orkestrel/scaffold'
|
|
967
1033
|
*
|
|
968
1034
|
* const compiler = new Compiler()
|
|
969
1035
|
* const scaffolding = compiler.compile(createBlueprint('router', { src: ['core'] }))
|
|
970
|
-
*
|
|
1036
|
+
*
|
|
1037
|
+
* scaffolding.plan?.artifacts // every planned file, in group order
|
|
1038
|
+
* scaffolding.stages // one CompileRecord per stage that ran
|
|
971
1039
|
* compiler.destroy()
|
|
972
1040
|
* ```
|
|
973
1041
|
*/
|
|
@@ -1212,7 +1280,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1212
1280
|
server: "export const appServer = (): UserConfig => ({\n\tresolve,\n\tpublicDir: false,\n\tplugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],\n\tbuild: {\n\t\temptyOutDir: true,\n\t\tlib: {\n\t\t\tentry: resolveWorkspacePath('app/server/main.ts'),\n\t\t\tformats: ['cjs'],\n\t\t\tfileName: () => 'main.cjs',\n\t\t},\n\t\toutDir: resolveWorkspacePath('dist/app/server'),\n\t\ttarget: 'node22',\n\t\trolldownOptions: {\n\t\t\tonLog: enforceBuildLog,\n\t\t\texternal: (id: string) => id.startsWith('node:'),\n\t\t},\n\t},\n\ttest: {\n\t\tname: { label: 'app:server', color: 'green' },\n\t\tinclude: ['tests/app/server/**/*.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t},\n})\n";
|
|
1213
1281
|
}>;
|
|
1214
1282
|
policy: "export const policy = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'policy', color: 'white' },\n\t\tinclude: ['tests/policy.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t},\n})\n";
|
|
1215
|
-
config: "export const config = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'config', color: 'yellow' },\n\t\tinclude: ['tests/config.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t\t// A config test validates every target wrapper
|
|
1283
|
+
config: "export const config = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'config', color: 'yellow' },\n\t\tinclude: ['tests/config.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t\t// A config test validates every target wrapper, spawns the real linter twice under\n\t\t// 15-second child caps, and rolls one face up through the compiler and the extractor it\n\t\t// spawns, so this budget clears the capped pair with room for a contended host.\n\t\ttestTimeout: 60_000,\n\t},\n})\n";
|
|
1216
1284
|
setup: "export const setup = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'setup', color: 'white' },\n\t\tinclude: ['tests/setup*.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t},\n})\n";
|
|
1217
1285
|
guides: "export const guides = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'guides', color: 'green' },\n\t\tinclude: ['tests/guides.test.ts'],\n\t\texclude: ['tests/src/**/*.test.ts', 'tests/app/**/*.test.ts', 'tests/setup.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t},\n})\n";
|
|
1218
1286
|
conformance: "// Where this package drifts from the official tooling it stays compatible with.\n// The subject is this package, so the proof is hermetic and stays in `npm test`.\nexport const conformance = (): UserConfig => ({\n\tresolve,\n\ttest: {\n\t\tname: { label: 'conformance', color: 'magenta' },\n\t\tinclude: ['tests/conformance.test.ts'],\n\t\tsetupFiles: ['./tests/setup.ts'],\n\t\tenvironment: 'node',\n\t\tbrowser: { enabled: false },\n\t},\n})\n";
|
|
@@ -1236,9 +1304,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1236
1304
|
}>;
|
|
1237
1305
|
vites: Readonly<{
|
|
1238
1306
|
src: Readonly<{
|
|
1239
|
-
core: "import { defineConfig, mergeConfig } from 'vite'\nimport
|
|
1240
|
-
browser: "import { defineConfig, mergeConfig } from 'vite'\nimport
|
|
1241
|
-
server: "import { defineConfig, mergeConfig } from 'vite'\nimport
|
|
1307
|
+
core: "import { defineConfig, mergeConfig } from 'vite'\nimport { declarationRollup, environmentBoundary, outputBoundary } from '../helpers.js'\nimport { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'\n\nexport default defineConfig(\n\tmergeConfig(srcCore(), {\n\t\tpublicDir: false,\n\t\tplugins: [\n\t\t\toutputBoundary('dist/src/core'),\n\t\t\tenvironmentBoundary('src/core'),\n\t\t\tdeclarationRollup({\n\t\t\t\tproject: resolveWorkspacePath('configs/src/tsconfig.core.json'),\n\t\t\t\ttypes: ['node'],\n\t\t\t}),\n\t\t],\n\t\tbuild: {\n\t\t\tlib: {\n\t\t\t\tentry: resolveWorkspacePath('src/core/index.ts'),\n\t\t\t\tformats: ['es', 'cjs'],\n\t\t\t\tfileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),\n\t\t\t},\n\t\t\toutDir: 'dist/src/core',\n\t\t\trolldownOptions: {\n\t\t\t\texternal: (id: string) =>\n\t\t\t\t\tid.startsWith('node:') ||\n\t\t\t\t\tid.startsWith('@orkestrel/') ||\n\t\t\t\t\tpeers.some((peer) => id === peer || id.startsWith(peer + '/')),\n\t\t\t},\n\t\t},\n\t}),\n)\n";
|
|
1308
|
+
browser: "import { defineConfig, mergeConfig } from 'vite'\nimport { declarationRollup, rewriteCoreSpecifier } from '../helpers.js'\nimport { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'\n\n// The roll-up reaches src/core through a specifier the tarball does not carry, so the rewrite\n// externalizes core through the package's own published root export, on the final roll-up alone.\nexport default defineConfig(\n\tmergeConfig(srcBrowser(), {\n\t\tplugins: [\n\t\t\tdeclarationRollup({\n\t\t\t\tproject: resolveWorkspacePath('configs/src/tsconfig.browser.json'),\n\t\t\t\trewrite: rewriteCoreSpecifier,\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
|
|
1309
|
+
server: "import { defineConfig, mergeConfig } from 'vite'\nimport { declarationRollup, rewriteCoreSpecifier } from '../helpers.js'\nimport { srcServer, resolveWorkspacePath } from '../../vite.config.ts'\n\n// The roll-up reaches src/core through a specifier the tarball does not carry, so the rewrite\n// externalizes core through the package's own published root export, on the final roll-up alone.\nexport default defineConfig(\n\tmergeConfig(srcServer(), {\n\t\tplugins: [\n\t\t\tdeclarationRollup({\n\t\t\t\tproject: resolveWorkspacePath('configs/src/tsconfig.server.json'),\n\t\t\t\trewrite: rewriteCoreSpecifier,\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
|
|
1242
1310
|
}>;
|
|
1243
1311
|
bin: "import { defineConfig, mergeConfig } from 'vite'\nimport { srcBin } from '../../vite.config.ts'\n\n// The `scaffold` executable build — a single ESM lib file, no declarations (an\n// executable ships no types), with the `#!/usr/bin/env node` shebang re-emitted through\n// `output.banner` (rolldown strips shebangs from source during bundling), and\n// `output.paths` rewriting the externalized `@src/*` specifiers to the built sibling\n// src environments (relative to `dist/bin/`), so the emitted bin resolves at runtime.\nexport default defineConfig(\n\tmergeConfig(srcBin(), {\n\t\tbuild: {\n\t\t\trolldownOptions: {\n\t\t\t\toutput: {\n\t\t\t\t\tbanner: '#!/usr/bin/env node',\n{{paths}}\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}),\n)\n";
|
|
1244
1312
|
app: Readonly<{
|
|
@@ -1317,19 +1385,28 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1317
1385
|
* and let the answers disagree, so a blueprint the gate will
|
|
1318
1386
|
* refuse is still constructible.
|
|
1319
1387
|
*
|
|
1320
|
-
* @example
|
|
1388
|
+
* @example Blueprint
|
|
1321
1389
|
* ```ts
|
|
1322
1390
|
* import { createBlueprint } from '@orkestrel/scaffold'
|
|
1323
1391
|
*
|
|
1324
|
-
* createBlueprint('router', {
|
|
1325
|
-
*
|
|
1392
|
+
* const blueprint = createBlueprint('router', {
|
|
1393
|
+
* src: ['core', 'server'],
|
|
1394
|
+
* dependencies: [{ name: '@orkestrel/emitter', range: '^0.0.5' }],
|
|
1395
|
+
* bin: true,
|
|
1396
|
+
* })
|
|
1397
|
+
*
|
|
1398
|
+
* blueprint.version // '0.0.1'
|
|
1399
|
+
* blueprint.engines // '>=22.12.0'
|
|
1326
1400
|
* ```
|
|
1327
1401
|
*/
|
|
1328
1402
|
export declare function createBlueprint(name: string, input?: Partial<Omit<Blueprint, 'name'>>): Blueprint;
|
|
1329
1403
|
|
|
1330
1404
|
/**
|
|
1331
|
-
* Lists the development dependencies that
|
|
1332
|
-
*
|
|
1405
|
+
* Lists the development dependencies that roll declarations up for published source.
|
|
1406
|
+
*
|
|
1407
|
+
* @remarks
|
|
1408
|
+
* The toolchain runs the compiler it already installs as a command, emitting one declaration per
|
|
1409
|
+
* module, and the extractor rolls that emit into the single file each published face ships.
|
|
1333
1410
|
*/
|
|
1334
1411
|
export declare const DECLARATION_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
1335
1412
|
|
|
@@ -1399,7 +1476,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1399
1476
|
*/
|
|
1400
1477
|
export declare const DEPENDENCY_NAME_PATTERN: RegExp;
|
|
1401
1478
|
|
|
1402
|
-
/** Describes the
|
|
1479
|
+
/** Describes the runtime and development sections a range-writing operation may change. */
|
|
1403
1480
|
export declare interface DependencyPinSet {
|
|
1404
1481
|
readonly runtime: readonly Dependency[];
|
|
1405
1482
|
readonly development: readonly Dependency[];
|
|
@@ -1591,7 +1668,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1591
1668
|
*/
|
|
1592
1669
|
export declare const GROUPS: readonly Group[];
|
|
1593
1670
|
|
|
1594
|
-
/** Names the guide-parity
|
|
1671
|
+
/** Names the package-owned guide-parity entry used by `test:guides` and to select the `guides` project. */
|
|
1595
1672
|
export declare const GUIDES_TEST_PATH = "tests/guides.test.ts";
|
|
1596
1673
|
|
|
1597
1674
|
/** Matches exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. */
|
|
@@ -1606,10 +1683,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1606
1683
|
* @remarks
|
|
1607
1684
|
* These are the files the fleet shares verbatim, and each target holds a copy
|
|
1608
1685
|
* of the paths it selects: the licence, the harness permission file, the
|
|
1609
|
-
* session-start hooks, the shared policy
|
|
1610
|
-
* the shared policy
|
|
1611
|
-
* byte-identical root dotfiles, and the
|
|
1612
|
-
* starts from. A directory entry vendors
|
|
1686
|
+
* session-start hooks, the shared policy
|
|
1687
|
+
* register, the shared policy proof, the shared policy plugin, the shared
|
|
1688
|
+
* configuration leaf and its proof, the byte-identical root dotfiles, and the
|
|
1689
|
+
* guide mirrors a generated workspace starts from. A directory entry vendors
|
|
1690
|
+
* everything beneath it.
|
|
1613
1691
|
*
|
|
1614
1692
|
* A plan carries the subset its target selects, which is why the list is a
|
|
1615
1693
|
* candidate set rather than a plan: a workspace never mirrors its own guide.
|
|
@@ -1618,7 +1696,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1618
1696
|
* its rules, its skills, its agent roles, its bench configuration, and its MCP
|
|
1619
1697
|
* registrations from {@link CANON_PATHS} inside the installed package, so no
|
|
1620
1698
|
* file scaffold leaves in a target names a path the target does not hold.
|
|
1621
|
-
* `
|
|
1699
|
+
* `blueprintToHostArtifacts` appends {@link CATALOG_AGENT_PATH} to what this list
|
|
1622
1700
|
* selects, which is what keeps the list itself disjoint from the canon.
|
|
1623
1701
|
*/
|
|
1624
1702
|
export declare const HOST_PATHS: readonly string[];
|
|
@@ -2150,7 +2228,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2150
2228
|
export declare const isQuestion: Guard<Question>;
|
|
2151
2229
|
|
|
2152
2230
|
/**
|
|
2153
|
-
* Checks whether a target's present bytes at a path
|
|
2231
|
+
* Checks whether another surface owns a target's present bytes at a path.
|
|
2154
2232
|
*
|
|
2155
2233
|
* @param path - The target-relative path to test.
|
|
2156
2234
|
* @returns True if the path is a {@link WORKSPACE_OWNED_PATHS} member or a
|
|
@@ -2212,7 +2290,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2212
2290
|
export declare function isSnapshot(value: unknown): value is Snapshot;
|
|
2213
2291
|
|
|
2214
2292
|
/**
|
|
2215
|
-
* Names
|
|
2293
|
+
* Names how an upstream lookup resolved: found, missing, unmatched, or failed.
|
|
2216
2294
|
*
|
|
2217
2295
|
* @remarks
|
|
2218
2296
|
* `found` carries the answer. `missing` is an upstream `404`, which is a
|
|
@@ -2228,7 +2306,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2228
2306
|
/** Names the manifest path every compiler plan emits with birth ownership. */
|
|
2229
2307
|
export declare const MANIFEST_PATH = "package.json";
|
|
2230
2308
|
|
|
2231
|
-
/** Describes the
|
|
2309
|
+
/** Describes the runtime, development, and peer sections read from an existing package manifest. */
|
|
2232
2310
|
export declare interface ManifestDependencySet {
|
|
2233
2311
|
readonly runtime: readonly Dependency[];
|
|
2234
2312
|
readonly development: readonly Dependency[];
|
|
@@ -2586,80 +2664,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2586
2664
|
export declare function nameToGuide(name: string): string;
|
|
2587
2665
|
|
|
2588
2666
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
*
|
|
2591
|
-
* @param name - The target workspace's own bare package name.
|
|
2592
|
-
* @returns One artifact per vendored path in `HOST_PATHS` order, then the
|
|
2593
|
-
* catalog file.
|
|
2594
|
-
*
|
|
2595
|
-
* @remarks
|
|
2596
|
-
* Every artifact is claimed by presence, which is the strongest claim a pure
|
|
2597
|
-
* compile can make: core cannot read the vendored data root, so it cannot carry
|
|
2598
|
-
* the bytes a content claim would have to be checked against. Reading that root
|
|
2599
|
-
* is what promotes the ones scaffold owns the bytes of.
|
|
2600
|
-
*
|
|
2601
|
-
* `source` is left absent because it falls back to `path`, and every vendored
|
|
2602
|
-
* path is stored under the name it is written to. The group comes from
|
|
2603
|
-
* {@link inferGroup}, so a vendored path and a foreign path found in a target
|
|
2604
|
-
* are classified by one rule and a plan never disagrees with the audit beside
|
|
2605
|
-
* it.
|
|
2606
|
-
*
|
|
2607
|
-
* {@link CATALOG_AGENT_PATH} is appended rather than listed, and it is the canon
|
|
2608
|
-
* path this compiler claims. The catalog verb refuses a target that lacks the
|
|
2609
|
-
* file, so the plan has to carry it; repair restores its absence from the
|
|
2610
|
-
* staged bytes; and the `.claude/agents` directory in `CANON_PATHS` is what
|
|
2611
|
-
* stages those bytes, so listing the file in `HOST_PATHS` as well would claim one
|
|
2612
|
-
* storage name twice and refuse the stage. The rest of the canon a target reads
|
|
2613
|
-
* from the installed package, at the locations the `AGENTS.md` and `CLAUDE.md`
|
|
2614
|
-
* pointers {@link blueprintToDocumentArtifacts} emits name.
|
|
2615
|
-
*
|
|
2616
|
-
* @example
|
|
2617
|
-
* ```ts
|
|
2618
|
-
* import { nameToHostArtifacts } from '@orkestrel/scaffold'
|
|
2619
|
-
*
|
|
2620
|
-
* nameToHostArtifacts('router').some((artifact) => artifact.path === '.claude/settings.json') // true
|
|
2621
|
-
* nameToHostArtifacts('router').some((artifact) => artifact.path === '.claude/agents/orkestrel.md') // true
|
|
2622
|
-
* nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
|
|
2623
|
-
* ```
|
|
2624
|
-
*/
|
|
2625
|
-
export declare function nameToHostArtifacts(name: string): readonly Artifact[];
|
|
2626
|
-
|
|
2627
|
-
/**
|
|
2628
|
-
* Derives the declaration rewrite a published face's `beforeWriteFile` applies.
|
|
2629
|
-
*
|
|
2630
|
-
* @param name - The workspace's own bare package name.
|
|
2631
|
-
* @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
|
|
2632
|
-
* fills its `{{replacement}}` span with, indented for that span.
|
|
2633
|
-
*
|
|
2634
|
-
* @remarks
|
|
2635
|
-
* `vite-plugin-dts` rolls a face into one declaration and keeps each source
|
|
2636
|
-
* module's own relative depth, so a nested module emits a path that escapes
|
|
2637
|
-
* `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
|
|
2638
|
-
* relative core path to the package's published root export, so the branch is
|
|
2639
|
-
* derived once here. The extension alternation is what the permitted import
|
|
2640
|
-
* spellings produce: an `@src/core` alias resolves to the core source module and
|
|
2641
|
-
* prints `.ts`, while a relative import prints the `.js` specifier it was
|
|
2642
|
-
* written with. The formatter keeps the call on one line only while the line it
|
|
2643
|
-
* prints measures inside the vendored width, and the workspace name is what
|
|
2644
|
-
* varies, so the shape is chosen by measuring the candidate: a tab prints as the
|
|
2645
|
-
* vendored two columns, and the gate admits a name long enough to push the
|
|
2646
|
-
* joined call past 100.
|
|
2647
|
-
*
|
|
2648
|
-
* @example
|
|
2649
|
-
* ```ts
|
|
2650
|
-
* import { nameToRewrite } from '@orkestrel/scaffold'
|
|
2651
|
-
*
|
|
2652
|
-
* nameToRewrite('router').includes("'@orkestrel/router'") // true
|
|
2653
|
-
* ```
|
|
2654
|
-
*/
|
|
2655
|
-
export declare function nameToRewrite(name: string): string;
|
|
2656
|
-
|
|
2657
|
-
/**
|
|
2658
|
-
* Lists the exact root filenames that wire an agent bench rather than the toolchain, frozen.
|
|
2667
|
+
* Lists the exact root paths that wire an agent bench or own an orchestration directory, frozen.
|
|
2659
2668
|
*
|
|
2660
2669
|
* @remarks
|
|
2661
|
-
* `.mcp.json` registers MCP servers for the harness.
|
|
2662
|
-
*
|
|
2670
|
+
* `.mcp.json` registers MCP servers for the harness. `scripts` is the owned
|
|
2671
|
+
* directory whose members wire the development environment.
|
|
2663
2672
|
*/
|
|
2664
2673
|
export declare const ORCHESTRATION_PATH_NAMES: readonly string[];
|
|
2665
2674
|
|
|
@@ -3019,7 +3028,8 @@ export declare class Compiler implements CompilerInterface {
|
|
|
3019
3028
|
export declare const RELEASE_PROOF_COMMAND = "npm run test:distribution -- --mode release";
|
|
3020
3029
|
|
|
3021
3030
|
/**
|
|
3022
|
-
* Replaces
|
|
3031
|
+
* Replaces the runtime and development dependency ranges in package manifest text, and never a
|
|
3032
|
+
* peer range.
|
|
3023
3033
|
*
|
|
3024
3034
|
* @param manifest - The manifest text to compile.
|
|
3025
3035
|
* @param pins - The runtime and development names and replacement ranges.
|
|
@@ -3135,12 +3145,12 @@ export declare class Compiler implements CompilerInterface {
|
|
|
3135
3145
|
*
|
|
3136
3146
|
* @example
|
|
3137
3147
|
* ```ts
|
|
3138
|
-
* import {
|
|
3148
|
+
* import { isScaffoldError, ScaffoldError } from '@orkestrel/scaffold'
|
|
3139
3149
|
*
|
|
3140
3150
|
* try {
|
|
3141
|
-
* throw new ScaffoldError('
|
|
3151
|
+
* throw new ScaffoldError('TARGET', 'The target carries no readable manifest.')
|
|
3142
3152
|
* } catch (error) {
|
|
3143
|
-
* if (isScaffoldError(error)) error.code // '
|
|
3153
|
+
* if (isScaffoldError(error)) error.code // 'TARGET'
|
|
3144
3154
|
* }
|
|
3145
3155
|
* ```
|
|
3146
3156
|
*/
|