@orkestrel/scaffold 0.0.103 → 0.0.105

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.
Files changed (94) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +84 -1
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +21 -6
  3. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +338 -0
  4. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +3 -39
  5. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +433 -47
  6. package/dist/agents/skills/orkestrel-publish/scripts/window.js +2 -1
  7. package/dist/bin/main.js +17 -10
  8. package/dist/bin/main.js.map +1 -1
  9. package/dist/host/agents/orchestration.md +6 -6
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +2 -2
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +1 -1
  12. package/dist/host/agents/skills/orkestrel-build/SKILL.md +2 -2
  13. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +2 -2
  14. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +1 -1
  15. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +3 -3
  16. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
  17. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +1 -0
  18. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +100 -1
  19. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +27 -6
  20. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -4
  21. package/dist/host/agents/skills/orkestrel-falsify/agents/openai.yaml +1 -1
  22. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +8 -7
  23. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +6 -6
  24. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +6 -1
  25. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +1700 -0
  26. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -2
  27. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +48 -0
  28. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +4 -2
  29. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +425 -0
  30. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +2 -2
  31. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +9 -8
  32. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +72 -0
  33. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +4 -0
  34. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +12 -0
  35. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +3 -3
  36. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +2 -2
  37. package/dist/host/agents/skills/orkestrel-publish/references/release.md +1 -0
  38. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +56 -13
  39. package/dist/host/agents/skills/orkestrel-publish/references/window.md +6 -6
  40. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +5 -49
  41. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +646 -49
  42. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +2 -1
  43. package/dist/host/agents/transports/codex.md +1 -1
  44. package/dist/host/claude/agents/orkestrel.md +54 -53
  45. package/dist/host/claude/agents/reviewer.md +4 -4
  46. package/dist/host/claude/agents/verifier.md +1 -1
  47. package/dist/host/claude/rules/documentation.md +2 -1
  48. package/dist/host/claude/rules/patterns.md +1 -1
  49. package/dist/host/claude/rules/quality.md +2 -2
  50. package/dist/host/claude/rules/styles.md +1 -1
  51. package/dist/host/claude/rules/tests.md +34 -8
  52. package/dist/host/claude/rules/typescript.md +29 -0
  53. package/dist/host/claude/rules/workspace.md +32 -22
  54. package/dist/host/claude/rules/writing.md +34 -26
  55. package/dist/host/claude/settings.json +2 -0
  56. package/dist/host/claude/skills/orkestrel-debrief/SKILL.md +1 -1
  57. package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +1 -1
  58. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +11 -0
  59. package/dist/host/codex/agents/analyst.toml +3 -3
  60. package/dist/host/codex/agents/reviewer.toml +4 -4
  61. package/dist/host/codex/agents/verifier.toml +3 -1
  62. package/dist/host/configs/policy.ts +13 -2
  63. package/dist/host/dotfiles/editorconfig +0 -1
  64. package/dist/host/dotfiles/gitattributes +0 -1
  65. package/dist/host/dotfiles/gitignore +0 -1
  66. package/dist/host/dotfiles/oxlintrc.json +24 -0
  67. package/dist/host/guides/agent.md +1674 -598
  68. package/dist/host/guides/budget.md +89 -29
  69. package/dist/host/guides/form.md +485 -99
  70. package/dist/host/guides/mcp.md +14 -2
  71. package/dist/host/guides/ollama.md +367 -86
  72. package/dist/host/guides/probe.md +1 -1
  73. package/dist/host/guides/scaffold.md +158 -101
  74. package/dist/host/guides/study.md +3185 -0
  75. package/dist/host/guides/table.md +239 -29
  76. package/dist/host/guides/terminal.md +22 -20
  77. package/dist/host/guides/test.md +776 -315
  78. package/dist/host/manifest.json +117 -65
  79. package/dist/host/tests/config.test.ts +136 -82
  80. package/dist/host/tests/policy.test.ts +41 -3
  81. package/dist/host/tests/setupPolicy.ts +87 -29
  82. package/dist/src/core/index.cjs +1389 -612
  83. package/dist/src/core/index.cjs.map +1 -1
  84. package/dist/src/core/index.d.cts +35 -20
  85. package/dist/src/core/index.d.ts +35 -20
  86. package/dist/src/core/index.js +1387 -613
  87. package/dist/src/core/index.js.map +1 -1
  88. package/dist/src/server/index.cjs +12 -12
  89. package/dist/src/server/index.cjs.map +1 -1
  90. package/dist/src/server/index.d.cts +12 -12
  91. package/dist/src/server/index.d.ts +12 -12
  92. package/dist/src/server/index.js +12 -12
  93. package/dist/src/server/index.js.map +1 -1
  94. package/package.json +5 -5
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  var package_default = {
5
5
  name: "@orkestrel/scaffold",
6
- version: "0.0.103",
6
+ version: "0.0.105",
7
7
  description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
8
8
  keywords: [
9
9
  "audit",
@@ -103,11 +103,11 @@ var package_default = {
103
103
  },
104
104
  devDependencies: {
105
105
  "@microsoft/api-extractor": "^7.59.4",
106
- "@orkestrel/browser": "^0.0.28",
107
- "@orkestrel/guide": "^0.0.24",
106
+ "@orkestrel/browser": "^0.0.29",
107
+ "@orkestrel/guide": "^0.0.25",
108
108
  "@orkestrel/html": "^0.0.12",
109
- "@orkestrel/probe": "^0.0.23",
110
- "@orkestrel/test": "^0.0.27",
109
+ "@orkestrel/probe": "^0.0.24",
110
+ "@orkestrel/test": "^0.0.29",
111
111
  "@types/node": "^26.6.4",
112
112
  "@vitest/browser-playwright": "^4.1.11",
113
113
  "oxfmt": "^0.72.0",
@@ -249,6 +249,8 @@ var APP_MATRIX = Object.freeze({
249
249
  var BIN_CONFIGS = Object.freeze(["configs/src/vite.bin.config.ts", "configs/src/tsconfig.bin.json"]);
250
250
  /** Names the executable entry whose presence makes a workspace `bin`. */
251
251
  var BIN_ENTRY_PATH = "src/bin/main.ts";
252
+ /** Names the built executable a bin workspace's manifest command runs. */
253
+ var BIN_OUTPUT_PATH = "dist/bin/main.js";
252
254
  /**
253
255
  * Lists the paths a target receives from the vendored data root, frozen.
254
256
  *
@@ -467,6 +469,7 @@ var TARGET_SKILL_NAMES = Object.freeze([
467
469
  "orkestrel-falsify",
468
470
  "orkestrel-harden",
469
471
  "orkestrel-journey",
472
+ "orkestrel-parallel",
470
473
  "orkestrel-polish"
471
474
  ]);
472
475
  /** Names the inventory skeleton a workspace with declared service vendors is given once. */
@@ -477,6 +480,8 @@ var GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
477
480
  var GUIDES_TEST_PATH = "tests/guides.test.ts";
478
481
  /** Names the generated packed-package proof every publishing workspace is planned at. */
479
482
  var DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
483
+ /** Names the content-owned harness imported by the generated distribution proof. */
484
+ var DISTRIBUTION_SETUP_PATH = "tests/setupDistribution.ts";
480
485
  /**
481
486
  * Names the `prepublishOnly` row that runs the packed-package proof against a real registry.
482
487
  *
@@ -495,6 +500,8 @@ var INTEGRATION_TEST_PATH = "tests/integration.test.ts";
495
500
  var MANIFEST_PATH = "package.json";
496
501
  /** Names the official-tooling drift proof whose presence makes a workspace `conformance`. */
497
502
  var CONFORMANCE_TEST_PATH = "tests/conformance.test.ts";
503
+ /** Names the real-tree proof whose presence makes a workspace `tree`. */
504
+ var TREE_TEST_PATH = "tests/tree.test.ts";
498
505
  /** Names the live-service readiness module whose presence makes a workspace `service`. */
499
506
  var SERVICE_SETUP_PATH = "tests/setupService.ts";
500
507
  /**
@@ -526,8 +533,8 @@ var NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
526
533
  * @remarks
527
534
  * A dependency name reaches a path, because a workspace's guide mirror is
528
535
  * derived from it. Fixing the scope and forbidding everything but the bare name
529
- * after it is what stops a hand-built name from escaping the directory the
530
- * mirror belongs in.
536
+ * after it is what stops a hand-built name from resolving outside the directory
537
+ * the mirror belongs in.
531
538
  */
532
539
  var DEPENDENCY_NAME_PATTERN = /^@orkestrel\/[a-z][a-z0-9-]*$/;
533
540
  /**
@@ -941,7 +948,13 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
941
948
  name: { label: name, color: 'magenta' },
942
949
  root: resolveWorkspacePath('.'),
943
950
  isolate: false,
944
- setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts', './tests/setupStyles.ts'],
951
+ setupFiles: [
952
+ './tests/setup.ts',
953
+ './tests/setupBrowser.ts',
954
+ ...(existsSync(resolveWorkspacePath('tests/setupStyles.ts'))
955
+ ? ['./tests/setupStyles.ts']
956
+ : []),
957
+ ],
945
958
  browser: {
946
959
  enabled: true,
947
960
  provider: playwright(browserOptions),
@@ -1461,6 +1474,24 @@ export function skills(override?: UserConfig): UserConfig {
1461
1474
  }
1462
1475
  return mergeOverride(project, override)
1463
1476
  }
1477
+ `,
1478
+ tree: `// The laws this package states about its own real tree.
1479
+ // Standalone Node proof; reads the tree without a build, install, or service.
1480
+ export function tree(override?: UserConfig): UserConfig {
1481
+ const project: UserConfig = {
1482
+ resolve,
1483
+ test: {
1484
+ name: { label: 'tree', color: 'magenta' },
1485
+ include: ['${TREE_TEST_PATH}'],
1486
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
1487
+ pool: 'threads',
1488
+ isolate: false,
1489
+ environment: 'node',
1490
+ browser: { enabled: false },
1491
+ },
1492
+ }
1493
+ return mergeOverride(project, override)
1494
+ }
1464
1495
  `,
1465
1496
  conformance: `// Where this package drifts from the official tooling it stays compatible with.
1466
1497
  // The subject is this package, so the proof is hermetic and stays in \`npm test\`.
@@ -2225,11 +2256,11 @@ export function resolveSystemBrowser(
2225
2256
  * verified system channel, then the platform default channel. An operator override outranks
2226
2257
  * discovery and is returned exactly as given: none of those environment values is checked
2227
2258
  * against the filesystem, because verifying an override would defeat the override. The pinned
2228
- * managed revision outranks anything found on the host because it is deterministic. The installed
2229
- * pinned revision returns empty options unless \`PLAYWRIGHT_SCROLLBARS=classic\` is set. Only a
2230
- * discovered system channel is verified before it is named. The platform default is unverified
2231
- * as well and exists only as a last resort: Windows takes \`msedge\`, which ships with the OS and
2232
- * never collides with a foreground Chrome.
2259
+ * managed revision outranks anything found on the host because it is deterministic. Every resolved
2260
+ * managed path names the full Chromium executable so headless launches retain browser permissions.
2261
+ * Only a discovered system channel is verified before it is named. The platform default is
2262
+ * unverified as well and exists only as a last resort: Windows takes \`msedge\`, which ships with the
2263
+ * OS and never collides with a foreground Chrome.
2233
2264
  *
2234
2265
  * Set \`PLAYWRIGHT_SCROLLBARS=classic\` only for runs that measure scrollbar compensation, never
2235
2266
  * for a standing gate. Every Chromium launch in that run omits Playwright's \`--hide-scrollbars\`
@@ -2276,7 +2307,6 @@ export function resolveBrowser(
2276
2307
  }
2277
2308
  const managed = pinned === undefined ? undefined : resolveManagedBrowser(pinned)
2278
2309
  if (managed !== undefined) {
2279
- if (managed === pinned) return scrollbars === 'classic' ? { launchOptions: launch } : {}
2280
2310
  return { launchOptions: { executablePath: managed, ...launch } }
2281
2311
  }
2282
2312
  const bundled = resolveBundledBrowser(platform, root)
@@ -2450,8 +2480,8 @@ declare module 'vitest' {
2450
2480
  }
2451
2481
  `,
2452
2482
  sheet: `import sheet from '{{root}}dist/src/{{name}}/index.css?raw'
2483
+ import { adoptSheet, readLayerNames } from '@orkestrel/test/browser'
2453
2484
  import { describe, expect, it } from 'vitest'
2454
- import { adoptSheet, readLayerNames } from '{{setup}}'
2455
2485
 
2456
2486
  describe('{{name}} entry', () => {
2457
2487
  it('opens with the declared cascade order', () => {
@@ -2475,120 +2505,8 @@ describe('{{name}} entry', () => {
2475
2505
  })
2476
2506
  `,
2477
2507
  styles: Object.freeze({
2478
- module: `/** Describes an adopted stylesheet and its idempotent release operation. */
2479
- export interface SheetAdoption {
2480
- readonly sheet: CSSStyleSheet
2481
- release(): void
2482
- }
2483
-
2484
- /**
2485
- * Adopts CSS text into the document with an idempotent release operation.
2486
- * @param text - The compiled sheet text.
2487
- * @returns The adopted sheet and its release operation.
2488
- * @example
2489
- * \`\`\`ts
2490
- * const adopted = adoptSheet('.sample { color: red }')
2491
- * adopted.release()
2492
- * \`\`\`
2493
- */
2494
- export function adoptSheet(text: string): SheetAdoption {
2495
- const sheet = new CSSStyleSheet()
2496
- sheet.replaceSync(text)
2497
- document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]
2498
- return {
2499
- sheet,
2500
- release: () => {
2501
- document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
2502
- (candidate) => candidate !== sheet,
2503
- )
2504
- },
2505
- }
2506
- }
2507
-
2508
- /**
2509
- * Reads layer statements and blocks in declaration order, including repeated names.
2510
- * @param source - The parsed sheet, or a grouping rule inside it.
2511
- * @param layer - The qualified name of the layer that encloses \`source\`, when one does.
2512
- * @returns Qualified layer names, with an empty name for an anonymous layer.
2513
- * @example
2514
- * \`\`\`ts
2515
- * readLayerNames(new CSSStyleSheet()) // []
2516
- * \`\`\`
2517
- */
2518
- export function readLayerNames(
2519
- source: CSSStyleSheet | CSSGroupingRule,
2520
- layer?: string,
2521
- ): readonly string[] {
2522
- return Array.from(source.cssRules).flatMap((rule) => [
2523
- ...(rule instanceof CSSLayerStatementRule
2524
- ? rule.nameList
2525
- : rule instanceof CSSLayerBlockRule
2526
- ? [rule.name]
2527
- : []
2528
- ).map((name) => (layer === undefined ? name : layer + '.' + name)),
2529
- ...(rule instanceof CSSGroupingRule
2530
- ? readLayerNames(
2531
- rule,
2532
- rule instanceof CSSLayerBlockRule
2533
- ? layer === undefined
2534
- ? rule.name
2535
- : layer + '.' + rule.name
2536
- : layer,
2537
- )
2538
- : []),
2539
- ])
2540
- }
2541
- `,
2542
- proof: `import type { SheetAdoption } from './setupStyles.js'
2543
- import { describe, expect, it } from 'vitest'
2544
- import { adoptSheet, readLayerNames } from './setupStyles.js'
2545
-
2546
- describe('CSSOM instruments', () => {
2547
- it('adopts and releases only its own sheet, including repeated release', () => {
2548
- const previous = [...document.adoptedStyleSheets]
2549
- const first: SheetAdoption = adoptSheet('.sample { color: red }')
2550
- try {
2551
- const second = adoptSheet('.other { display: block }')
2552
- try {
2553
- expect(document.adoptedStyleSheets).toEqual([...previous, first.sheet, second.sheet])
2554
- expect(first.sheet.cssRules.length).toBe(1)
2555
- first.release()
2556
- first.release()
2557
- expect(document.adoptedStyleSheets).toEqual([...previous, second.sheet])
2558
- } finally {
2559
- second.release()
2560
- }
2561
- } finally {
2562
- first.release()
2563
- }
2564
- expect(document.adoptedStyleSheets).toEqual(previous)
2565
- })
2566
-
2567
- it('reads statements, blocks, nested conditions, and anonymous layers in order', () => {
2568
- const adopted = adoptSheet(
2569
- '@layer first, second; @layer first { @media all { @layer nested {} } } @layer {}',
2570
- )
2571
- try {
2572
- expect(readLayerNames(adopted.sheet)).toEqual([
2573
- 'first',
2574
- 'second',
2575
- 'first',
2576
- 'first.nested',
2577
- '',
2578
- ])
2579
- } finally {
2580
- adopted.release()
2581
- }
2582
- })
2583
-
2584
- it('returns no layer names for an empty sheet or an unlayered rule', () => {
2585
- const sheet = new CSSStyleSheet()
2586
- expect(readLayerNames(sheet)).toEqual([])
2587
- sheet.replaceSync('.sample { color: red }')
2588
- expect(readLayerNames(sheet)).toEqual([])
2589
- })
2590
- })
2591
- `
2508
+ module: "",
2509
+ proof: ""
2592
2510
  }),
2593
2511
  setup: "",
2594
2512
  global: Object.freeze({
@@ -2624,13 +2542,7 @@ describe('bin entry', () => {
2624
2542
  })
2625
2543
  `,
2626
2544
  distribution: Object.freeze({
2627
- proof: `// The artifact a consumer installs, measured rather than described. This workspace
2628
- // is packed and installed into a throwaway consumer, and every following claim is read
2629
- // off that installed tree: the exports map it publishes, the declarations it ships,
2630
- // and the module objects a real runtime hands a consumer. Nothing here names this
2631
- // package, one of its exports, or how many there are, so the proof stays true as
2632
- // the published surface moves.
2633
- {{types}}import type { SpawnSyncReturns } from 'node:child_process'
2545
+ module: `{{types}}import type { SpawnSyncReturns } from 'node:child_process'
2634
2546
  import type { TestContext } from 'vitest'
2635
2547
  import { spawnSync } from 'node:child_process'
2636
2548
  import {
@@ -2645,67 +2557,126 @@ import {
2645
2557
  } from 'node:fs'
2646
2558
  {{transport}}import { createRequire } from 'node:module'
2647
2559
  import { tmpdir } from 'node:os'
2648
- import { dirname, join, resolve } from 'node:path'
2649
- import { fileURLToPath } from 'node:url'
2650
- {{launcher}}import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2560
+ import { dirname, join, relative as relativePath, resolve, sep } from 'node:path'
2561
+ import { fileURLToPath, pathToFileURL } from 'node:url'
2562
+ import { isDeepStrictEqual } from 'node:util'
2563
+ import { parseSync } from 'vite'
2564
+ {{launcher}}
2565
+ /** Defines the lifecycle and installed consumer read by a distribution proof. */
2566
+ export interface DistributionSuiteInterface {
2567
+ readonly scratch: string
2568
+ start(): void
2569
+ read(context: TestContext): DistributionStage
2570
+ destroy(): void
2571
+ }
2651
2572
 
2652
- const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
2653
- const NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'
2573
+ /** Holds the observed and required values for one distribution assertion. */
2574
+ export interface DistributionObservation {
2575
+ readonly actual: unknown
2576
+ readonly expected: unknown
2577
+ }
2578
+
2579
+ /** Defines one independently registered distribution observation. */
2580
+ export interface DistributionCase {
2581
+ readonly label: string
2582
+ readonly observe: (
2583
+ context: TestContext,
2584
+ ) => DistributionObservation | Promise<DistributionObservation>
2585
+ }
2586
+
2587
+ /** Holds the installed export map's classified publications. */
2588
+ export interface DistributionClassification {
2589
+ readonly entries: readonly DistributionEntry[]
2590
+ readonly subpaths: readonly string[]
2591
+ readonly undeclared: readonly string[]
2592
+ readonly excluded: readonly string[]
2593
+ readonly targets: readonly string[]
2594
+ }
2595
+
2596
+ /** Names the workspace whose packed artifact the proof consumes. */
2597
+ export const DISTRIBUTION_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
2598
+ /** Names the npm executable on the running host. */
2599
+ export const DISTRIBUTION_NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'
2654
2600
  // The compiler this workspace installs, run as a command rather than called in
2655
2601
  // process: the command and its plain-text diagnostics are the same across the
2656
2602
  // compiler majors this toolchain supports, and its in-process API is not. It is
2657
2603
  // resolved from the workspace under proof, so a consumer of the packed artifact is
2658
2604
  // checked by the same compiler that workspace's own \`check\` script runs.
2659
- const TSC = createRequire(join(ROOT, 'package.json')).resolve('typescript/bin/tsc')
2605
+ /** Names the compiler installed by the workspace under proof. */
2606
+ export const DISTRIBUTION_COMPILER = createRequire(join(DISTRIBUTION_ROOT, 'package.json')).resolve(
2607
+ 'typescript/bin/tsc',
2608
+ )
2660
2609
  // Windows needs a shell to launch a \`.cmd\`: Node refuses one directly since the
2661
2610
  // batch-argument hardening, and \`spawnSync\` returns \`EINVAL\` with a null status
2662
2611
  // rather than an exit code a caller can read. Every following argument is a literal or
2663
2612
  // a path this file built, so the shell has nothing to escape.
2664
- const SHELL = process.platform === 'win32'
2613
+ /** Selects the Windows command-shell route required by the npm shim. */
2614
+ export const DISTRIBUTION_SHELL = process.platform === 'win32'
2665
2615
  // \`prepublishOnly\` runs this proof as \`npm run test:distribution -- --mode release\`,
2666
2616
  // and the root configuration's project factories carry that mode into this project.
2667
2617
  // Release is the publish gate, so evidence it cannot obtain fails there and skips
2668
2618
  // everywhere else: a gate that passes on missing evidence proves nothing.
2669
- const RELEASE = import.meta.env.MODE === 'release'
2619
+ /** Indicates whether missing prerequisites must fail the publish gate. */
2620
+ export const DISTRIBUTION_RELEASE = import.meta.env?.MODE === 'release'
2670
2621
  // The built output directory convention a browser face may publish from. Every
2671
2622
  // selection reads this prefix off the export target and never off the subpath name. A
2672
2623
  // workspace whose only published face is the browser one publishes that face at the
2673
2624
  // root subpath, so a rule keyed on the subpath name drives a browser bundle through
2674
2625
  // Node and the miss is silent.
2675
- const BROWSER_OUTPUT = './dist/src/browser/'
2676
- const ABSENT_SUBPATH = '/no-subpath-is-published-under-this-name'
2626
+ /** Names the conventional output prefix for a browser publication. */
2627
+ export const DISTRIBUTION_BROWSER_OUTPUT = './dist/src/browser/'
2628
+ /** Names the unpublished subpath used as a resolution control. */
2629
+ export const DISTRIBUTION_ABSENT_SUBPATH = '/no-subpath-is-published-under-this-name'
2677
2630
  // A compiler diagnostic that says where it is: the path relative to the directory
2678
2631
  // the compiler ran in, the 1-based line and column, the code, and the message. The
2679
2632
  // diagnostics are the verdict rather than the exit code, which differs between the
2680
2633
  // compiler majors this toolchain supports, so a reported line matching nothing here
2681
2634
  // came from something other than a check of a consumer module.
2682
- const DIAGNOSTIC_PATTERN = /^(.+?)\\(\\d+,\\d+\\): error TS\\d+: /u
2683
- const PING = ['ping', '--fetch-retries=0', '--fetch-timeout=5000', '--loglevel=silent']
2684
- const ESM_DRIVER = 'drive.mjs'
2685
- const CJS_DRIVER = 'drive.cjs'
2686
- const CONSUMER_MANIFEST = \`{ "name": "distribution-consumer", "private": true, "type": "module" }\\n\`
2687
- const ESM_DRIVER_SOURCE = \`const entry = await import(process.argv[2])
2635
+ /** Matches the location prefix of a consumer compiler diagnostic. */
2636
+ export const DISTRIBUTION_DIAGNOSTIC_PATTERN = /^(.+?)\\(\\d+,\\d+\\): error TS\\d+: /u
2637
+ /** Lists the bounded registry-readiness command arguments. */
2638
+ export const DISTRIBUTION_PING = Object.freeze([
2639
+ 'ping',
2640
+ '--fetch-retries=0',
2641
+ '--fetch-timeout=5000',
2642
+ '--loglevel=silent',
2643
+ ])
2644
+ /** Names the consumer entry that imports an installed module. */
2645
+ export const DISTRIBUTION_ESM_DRIVER = 'drive.mjs'
2646
+ /** Names the consumer entry that requires an installed module. */
2647
+ export const DISTRIBUTION_CJS_DRIVER = 'drive.cjs'
2648
+ /** Defines the isolated consumer package manifest. */
2649
+ export const DISTRIBUTION_CONSUMER_MANIFEST =
2650
+ '{ "name": "distribution-consumer", "private": true, "type": "module" }\\n'
2651
+ /** Reads the names exported by an installed ESM entry. */
2652
+ export const DISTRIBUTION_ESM_DRIVER_SOURCE = \`const entry = await import(process.argv[2])
2688
2653
  process.stdout.write(JSON.stringify(Object.keys(entry).sort()))
2689
2654
  \`
2690
- const CJS_DRIVER_SOURCE = \`const entry = require(process.argv[2])
2655
+ /** Reads the names exported by an installed CommonJS entry. */
2656
+ export const DISTRIBUTION_CJS_DRIVER_SOURCE = \`const entry = require(process.argv[2])
2691
2657
  process.stdout.write(JSON.stringify(Object.keys(entry).sort()))
2692
2658
  \`
2693
2659
 
2694
2660
  // The extensions a JavaScript handler loads as modules. Node loads a native addon
2695
2661
  // through its addon handler instead, so that extension is named separately.
2696
- const MODULE_EXTENSIONS = ['.js', '.mjs', '.cjs']
2697
- const ADDON_EXTENSION = '.node'
2662
+ /** Lists extensions loaded as JavaScript modules. */
2663
+ export const DISTRIBUTION_MODULE_EXTENSIONS = Object.freeze(['.js', '.mjs', '.cjs'])
2664
+ /** Names the extension loaded by the native-addon handler. */
2665
+ export const DISTRIBUTION_ADDON_EXTENSION = '.node'
2698
2666
  // The extensions a declaration file carries. A \`require\` condition declares
2699
2667
  // \`.d.cts\` and an ESM-only one \`.d.mts\`, so the \`.d.ts\` spelling alone does not
2700
2668
  // name them.
2701
- const DECLARATION_EXTENSIONS = ['.d.ts', '.d.cts', '.d.mts']
2702
- type Format = 'module' | 'commonjs'
2669
+ /** Lists the supported TypeScript declaration extensions. */
2670
+ export const DISTRIBUTION_DECLARATION_EXTENSIONS = Object.freeze(['.d.ts', '.d.cts', '.d.mts'])
2671
+ /** Names the module format of a typed consumer. */
2672
+ export type DistributionFormat = 'module' | 'commonjs'
2703
2673
 
2704
2674
  // The Node import target is resolved with the conditions that driver supplies. The
2705
2675
  // CommonJS compile probe is selected from its declaration's format, and its runtime
2706
2676
  // drive loads the same subpath through Node's require resolver. Vite's production
2707
2677
  // client build enables its module and browser conditions.
2708
- const RUNTIME_CONDITIONS = Object.freeze({
2678
+ /** Defines the conditions each runtime uses to resolve an entry. */
2679
+ export const DISTRIBUTION_RUNTIME_CONDITIONS = Object.freeze({
2709
2680
  module: Object.freeze(['node-addons', 'node', 'import', 'module-sync']),
2710
2681
  commonjs: Object.freeze(['node-addons', 'node', 'require', 'module-sync']),
2711
2682
  browser: Object.freeze(['module', 'browser', 'production', 'import']),
@@ -2713,24 +2684,28 @@ const RUNTIME_CONDITIONS = Object.freeze({
2713
2684
  // TypeScript's Node resolutions add \`node\` to the format condition. Its bundler
2714
2685
  // resolution does not, so a browser drive compares against the declaration a bundler
2715
2686
  // consumer reads rather than borrowing the Node declaration.
2716
- const BUNDLER_CONDITIONS = Object.freeze({
2717
- module: ['types', 'import'],
2718
- commonjs: ['types', 'require'],
2687
+ /** Defines the declaration conditions used by bundler consumers. */
2688
+ export const DISTRIBUTION_BUNDLER_CONDITIONS = Object.freeze({
2689
+ module: Object.freeze(['types', 'import']),
2690
+ commonjs: Object.freeze(['types', 'require']),
2719
2691
  })
2720
- const DECLARATION_CONDITIONS = Object.freeze({
2721
- module: ['types', 'node', 'import'],
2722
- commonjs: ['types', 'node', 'require'],
2723
- browser: BUNDLER_CONDITIONS.module,
2692
+ /** Defines the declaration conditions paired with each runtime. */
2693
+ export const DISTRIBUTION_DECLARATION_CONDITIONS = Object.freeze({
2694
+ module: Object.freeze(['types', 'node', 'import']),
2695
+ commonjs: Object.freeze(['types', 'node', 'require']),
2696
+ browser: DISTRIBUTION_BUNDLER_CONDITIONS.module,
2724
2697
  })
2725
2698
 
2726
- interface Resolution {
2699
+ /** Describes the compiler options and conditions for one consumer resolution. */
2700
+ export interface DistributionResolution {
2727
2701
  readonly label: string
2728
2702
  readonly resolution: string
2729
2703
  readonly module: string
2730
- readonly conditions: Readonly<Record<Format, readonly string[]>>
2704
+ readonly conditions: Readonly<Record<DistributionFormat, readonly string[]>>
2731
2705
  }
2732
2706
 
2733
- interface TargetResolution {
2707
+ /** Holds the target selected by package condition resolution. */
2708
+ export interface DistributionTargetResult {
2734
2709
  readonly target: string
2735
2710
  }
2736
2711
 
@@ -2739,44 +2714,53 @@ interface TargetResolution {
2739
2714
  // \`require\`-only subpath therefore stays in each CommonJS probe that can resolve it.
2740
2715
  // The option values are the spellings the project file takes, so nothing here needs
2741
2716
  // the compiler's own API to name them.
2742
- const RESOLUTIONS: readonly Resolution[] = [
2717
+ /** Lists the compiler resolutions driven against installed declarations. */
2718
+ export const DISTRIBUTION_RESOLUTIONS: readonly DistributionResolution[] = Object.freeze([
2743
2719
  {
2744
2720
  label: 'node16',
2745
2721
  resolution: 'node16',
2746
2722
  module: 'node16',
2747
- conditions: DECLARATION_CONDITIONS,
2723
+ conditions: DISTRIBUTION_DECLARATION_CONDITIONS,
2748
2724
  },
2749
2725
  {
2750
2726
  label: 'nodenext',
2751
2727
  resolution: 'nodenext',
2752
2728
  module: 'nodenext',
2753
- conditions: DECLARATION_CONDITIONS,
2729
+ conditions: DISTRIBUTION_DECLARATION_CONDITIONS,
2754
2730
  },
2755
2731
  {
2756
2732
  label: 'bundler',
2757
2733
  resolution: 'bundler',
2758
2734
  module: 'esnext',
2759
- conditions: BUNDLER_CONDITIONS,
2735
+ conditions: DISTRIBUTION_BUNDLER_CONDITIONS,
2760
2736
  },
2761
- ]
2737
+ ])
2762
2738
 
2763
2739
  // The driver a bundled consumer reads declarations under. A browser application
2764
2740
  // compiles through a bundler, so the browser drive answers under this one alone,
2765
2741
  // and naming it here is what keeps that selection tied to the driver it selects.
2766
- const BROWSER_DRIVER = RESOLUTIONS.find((candidate) => candidate.label === 'bundler')
2767
- if (BROWSER_DRIVER === undefined) throw new Error("RESOLUTIONS carries no 'bundler' row")
2742
+ /** Selects the compiler resolution used by a browser consumer. */
2743
+ export const DISTRIBUTION_BROWSER_RESOLUTION = DISTRIBUTION_RESOLUTIONS.find(
2744
+ (candidate) => candidate.label === 'bundler',
2745
+ )
2746
+ if (DISTRIBUTION_BROWSER_RESOLUTION === undefined)
2747
+ throw new Error("DISTRIBUTION_RESOLUTIONS carries no 'bundler' row")
2768
2748
 
2769
- const FORMATS: ReadonlyArray<readonly [extension: string, format: Format]> = [
2749
+ /** Pairs consumer source extensions with their module formats. */
2750
+ export const DISTRIBUTION_FORMATS: ReadonlyArray<
2751
+ readonly [extension: string, format: DistributionFormat]
2752
+ > = Object.freeze([
2770
2753
  ['ts', 'module'],
2771
2754
  ['cts', 'commonjs'],
2772
- ]
2755
+ ])
2773
2756
 
2774
2757
  // One published subpath, resolved to what this proof can drive: the specifier a
2775
2758
  // consumer writes, whether the declarations its consumer formats resolve at all,
2776
2759
  // whether the exports map answers the browser condition with a target of its own,
2777
2760
  // whether it answers \`import\` and \`require\` at all, and whether the target that
2778
2761
  // \`require\` answers with is one that a CommonJS consumer loads.
2779
- interface Entry {
2762
+ /** Describes one published subpath and its runtime and declaration capabilities. */
2763
+ export interface DistributionEntry {
2780
2764
  readonly subpath: string
2781
2765
  readonly specifier: string
2782
2766
  readonly mapping: unknown
@@ -2794,29 +2778,48 @@ interface Entry {
2794
2778
  // The installed tree every claim is read from. Every subpath the exports map names
2795
2779
  // lands in exactly one of \`entries\`, \`undeclared\`, and \`excluded\`, so a subpath this
2796
2780
  // proof cannot drive is reported rather than dropped.
2797
- interface Stage {
2781
+ /** Holds the installed consumer and the partition of its published subpaths. */
2782
+ export interface DistributionStage {
2798
2783
  readonly consumer: string
2799
2784
  readonly installed: string
2800
2785
  readonly archives: readonly string[]
2801
- readonly entries: readonly Entry[]
2786
+ readonly entries: readonly DistributionEntry[]
2802
2787
  readonly subpaths: readonly string[]
2803
2788
  readonly undeclared: readonly string[]
2804
2789
  readonly excluded: readonly string[]
2805
2790
  readonly targets: readonly string[]
2806
2791
  }
2807
2792
 
2808
- function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
2793
+ /**
2794
+ * Checks whether a value is a non-null object other than an array.
2795
+ *
2796
+ * @param value - The value to inspect.
2797
+ * @returns True if the value is a record; false otherwise.
2798
+ */
2799
+ export function isDistributionRecord(value: unknown): value is Readonly<Record<string, unknown>> {
2809
2800
  return typeof value === 'object' && value !== null && !Array.isArray(value)
2810
2801
  }
2811
2802
 
2812
- function isNames(value: unknown): value is readonly string[] {
2803
+ /**
2804
+ * Checks whether a value is an array of strings.
2805
+ *
2806
+ * @param value - The value to inspect.
2807
+ * @returns True if every array member is a string; false otherwise.
2808
+ */
2809
+ export function isDistributionNames(value: unknown): value is readonly string[] {
2813
2810
  return Array.isArray(value) && value.every((name) => typeof name === 'string')
2814
2811
  }
2815
2812
 
2816
2813
  // A fallback list, which is what Node reads an array in an exports entry as. The
2817
2814
  // narrowing is what the following walkers need: \`Array.isArray\` widens an \`unknown\`
2818
2815
  // member to \`any\`, and an entry read that way is not read at all.
2819
- function isList(value: unknown): value is readonly unknown[] {
2816
+ /**
2817
+ * Checks whether a value is a fallback array.
2818
+ *
2819
+ * @param value - The value to inspect.
2820
+ * @returns True if the value is an array; false otherwise.
2821
+ */
2822
+ export function isDistributionList(value: unknown): value is readonly unknown[] {
2820
2823
  return Array.isArray(value)
2821
2824
  }
2822
2825
 
@@ -2824,7 +2827,13 @@ function isList(value: unknown): value is readonly unknown[] {
2824
2827
  // package and a target containing a dot, parent, or node_modules segment during
2825
2828
  // package-target resolution. A later module-resolution failure is not the same
2826
2829
  // thing: an array falls through the former and keeps the latter.
2827
- function isPackageTarget(target: string): boolean {
2830
+ /**
2831
+ * Checks whether a relative package target contains only admitted segments.
2832
+ *
2833
+ * @param target - The package-relative target.
2834
+ * @returns True if the target passes package-target validation; false otherwise.
2835
+ */
2836
+ export function matchesDistributionPackageTarget(target: string): boolean {
2828
2837
  if (!target.startsWith('./')) return false
2829
2838
  const refused = ['.', '..', 'node_modules']
2830
2839
  return target
@@ -2839,59 +2848,113 @@ function isPackageTarget(target: string): boolean {
2839
2848
  })
2840
2849
  }
2841
2850
 
2842
- function readJson(path: string): unknown {
2851
+ /**
2852
+ * Reads and parses a JSON file.
2853
+ *
2854
+ * @param path - The file to read.
2855
+ * @returns The parsed value. Throws when reading or parsing fails.
2856
+ */
2857
+ export function readDistributionJSON(path: string): unknown {
2843
2858
  const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))
2844
2859
  return parsed
2845
2860
  }
2846
2861
 
2847
- function readManifestName(path: string): string {
2848
- const manifest = readJson(path)
2849
- if (!isRecord(manifest) || typeof manifest.name !== 'string') {
2862
+ /**
2863
+ * Reads the package name from a manifest.
2864
+ *
2865
+ * @param path - The manifest to read.
2866
+ * @returns The package name. Throws when the manifest has no string name.
2867
+ */
2868
+ export function readDistributionManifestName(path: string): string {
2869
+ const manifest = readDistributionJSON(path)
2870
+ if (!isDistributionRecord(manifest) || typeof manifest.name !== 'string') {
2850
2871
  throw new Error(\`The manifest at \${path} declares no package name\`)
2851
2872
  }
2852
2873
  return manifest.name
2853
2874
  }
2854
2875
 
2855
- function writeFile(path: string, content: string): void {
2876
+ /**
2877
+ * Writes a consumer file after creating its parent directory.
2878
+ *
2879
+ * @param path - The file to write.
2880
+ * @param content - The text to write.
2881
+ */
2882
+ export function writeDistributionFile(path: string, content: string): void {
2856
2883
  mkdirSync(dirname(path), { recursive: true })
2857
2884
  writeFileSync(path, content)
2858
2885
  }
2859
2886
 
2860
- function readOutput(result: SpawnSyncReturns<string>): string {
2887
+ /**
2888
+ * Combines the output streams of a completed command.
2889
+ *
2890
+ * @param result - The completed command.
2891
+ * @returns The trimmed diagnostic text.
2892
+ */
2893
+ export function readDistributionOutput(result: SpawnSyncReturns<string>): string {
2861
2894
  return \`\${result.stdout ?? ''}\${result.stderr ?? ''}\`.trim()
2862
2895
  }
2863
2896
 
2864
- function runNpm(args: readonly string[], cwd: string, scratch: string): SpawnSyncReturns<string> {
2865
- return spawnSync(NPM, [...args], {
2897
+ /**
2898
+ * Runs npm with a cache inside the owned scratch directory.
2899
+ *
2900
+ * @param args - The npm arguments.
2901
+ * @param cwd - The working directory.
2902
+ * @param scratch - The owned scratch root.
2903
+ * @returns The command status and captured streams.
2904
+ */
2905
+ export function runDistributionNpm(
2906
+ args: readonly string[],
2907
+ cwd: string,
2908
+ scratch: string,
2909
+ ): SpawnSyncReturns<string> {
2910
+ return spawnSync(DISTRIBUTION_NPM, [...args], {
2866
2911
  cwd,
2867
2912
  encoding: 'utf8',
2868
2913
  env: { ...process.env, npm_config_cache: join(scratch, 'cache') },
2869
- shell: SHELL,
2914
+ shell: DISTRIBUTION_SHELL,
2870
2915
  windowsHide: true,
2871
2916
  })
2872
2917
  }
2873
2918
 
2874
- function runNode(args: readonly string[], cwd: string): SpawnSyncReturns<string> {
2919
+ /**
2920
+ * Runs a JavaScript entry with the running Node executable.
2921
+ *
2922
+ * @param args - The Node arguments.
2923
+ * @param cwd - The working directory.
2924
+ * @returns The command status and captured streams.
2925
+ */
2926
+ export function runDistributionNode(
2927
+ args: readonly string[],
2928
+ cwd: string,
2929
+ ): SpawnSyncReturns<string> {
2875
2930
  return spawnSync(process.execPath, [...args], { cwd, encoding: 'utf8', windowsHide: true })
2876
2931
  }
2877
2932
 
2878
2933
  // Node's own condition matching, read in declaration order.
2879
- function resolvePackageTarget(
2934
+ /**
2935
+ * Resolves a package target through conditions and fallback arrays.
2936
+ *
2937
+ * @param entry - The exports-map entry.
2938
+ * @param conditions - The active resolution conditions.
2939
+ * @returns The selected target, or undefined when no branch resolves.
2940
+ */
2941
+ export function resolveDistributionPackageTarget(
2880
2942
  entry: unknown,
2881
2943
  conditions: readonly string[],
2882
- ): TargetResolution | undefined {
2944
+ ): DistributionTargetResult | undefined {
2883
2945
  if (typeof entry === 'string') return { target: entry }
2884
- if (isList(entry)) {
2946
+ if (isDistributionList(entry)) {
2885
2947
  for (const member of entry) {
2886
- const resolved = resolvePackageTarget(member, conditions)
2887
- if (resolved !== undefined && isPackageTarget(resolved.target)) return resolved
2948
+ const resolved = resolveDistributionPackageTarget(member, conditions)
2949
+ if (resolved !== undefined && matchesDistributionPackageTarget(resolved.target))
2950
+ return resolved
2888
2951
  }
2889
2952
  return undefined
2890
2953
  }
2891
- if (!isRecord(entry)) return undefined
2954
+ if (!isDistributionRecord(entry)) return undefined
2892
2955
  for (const [condition, nested] of Object.entries(entry)) {
2893
2956
  if (condition !== 'default' && !conditions.includes(condition)) continue
2894
- const resolved = resolvePackageTarget(nested, conditions)
2957
+ const resolved = resolveDistributionPackageTarget(nested, conditions)
2895
2958
  if (resolved !== undefined) return resolved
2896
2959
  }
2897
2960
  return undefined
@@ -2901,13 +2964,29 @@ function resolvePackageTarget(
2901
2964
  // one walker. An entry may declare \`types\` beside \`default\` at its top level
2902
2965
  // rather than inside \`import\`, so a fixed \`entry.import.types\` lookup is not
2903
2966
  // equivalent to condition resolution.
2904
- function resolveTarget(entry: unknown, conditions: readonly string[]): string | undefined {
2905
- return resolvePackageTarget(entry, conditions)?.target
2967
+ /**
2968
+ * Resolves the path selected by an exports-map entry.
2969
+ *
2970
+ * @param entry - The exports-map entry.
2971
+ * @param conditions - The active resolution conditions.
2972
+ * @returns The selected path, or undefined when no branch resolves.
2973
+ */
2974
+ export function resolveDistributionTarget(
2975
+ entry: unknown,
2976
+ conditions: readonly string[],
2977
+ ): string | undefined {
2978
+ return resolveDistributionPackageTarget(entry, conditions)?.target
2906
2979
  }
2907
2980
 
2908
2981
  // Whether a path is a physical file. TypeScript's file-existence check refuses a
2909
2982
  // directory at the same spelling and continues to the outer package scope.
2910
- function matchesFile(path: string): boolean {
2983
+ /**
2984
+ * Checks whether a path names a physical file.
2985
+ *
2986
+ * @param path - The filesystem path.
2987
+ * @returns True if the path names a file; false otherwise.
2988
+ */
2989
+ export function matchesDistributionFile(path: string): boolean {
2911
2990
  try {
2912
2991
  return statSync(path).isFile()
2913
2992
  } catch {
@@ -2918,8 +2997,18 @@ function matchesFile(path: string): boolean {
2918
2997
  // TypeScript resolves a declaration target by accepting an existing declaration
2919
2998
  // directly or by substituting beside a JavaScript target. A missing target leaves
2920
2999
  // the containing condition or fallback list unresolved, so the walk continues.
2921
- function targetToDeclaration(target: string, installed: string): string | undefined {
2922
- if (!isPackageTarget(target)) return undefined
3000
+ /**
3001
+ * Resolves an existing declaration beside a package target.
3002
+ *
3003
+ * @param target - The package-relative target.
3004
+ * @param installed - The installed package root.
3005
+ * @returns The declaration target, or undefined when no declaration exists.
3006
+ */
3007
+ export function targetToDistributionDeclaration(
3008
+ target: string,
3009
+ installed: string,
3010
+ ): string | undefined {
3011
+ if (!matchesDistributionPackageTarget(target)) return undefined
2923
3012
  const declaration = target.endsWith('.cjs')
2924
3013
  ? \`\${target.slice(0, -4)}.d.cts\`
2925
3014
  : target.endsWith('.mjs')
@@ -2927,29 +3016,37 @@ function targetToDeclaration(target: string, installed: string): string | undefi
2927
3016
  : target.endsWith('.js')
2928
3017
  ? \`\${target.slice(0, -3)}.d.ts\`
2929
3018
  : target
2930
- if (declaration === target && !isDeclaration(target)) return undefined
2931
- return matchesFile(join(installed, declaration)) ? declaration : undefined
3019
+ if (declaration === target && !matchesDistributionDeclaration(target)) return undefined
3020
+ return matchesDistributionFile(join(installed, declaration)) ? declaration : undefined
2932
3021
  }
2933
3022
 
2934
3023
  // The declaration TypeScript resolves through one importing format's conditions.
2935
3024
  // Condition objects keep manifest order, and arrays keep fallback order.
2936
- function resolveDeclaration(
3025
+ /**
3026
+ * Resolves an existing declaration through consumer conditions.
3027
+ *
3028
+ * @param entry - The exports-map entry.
3029
+ * @param conditions - The active declaration conditions.
3030
+ * @param installed - The installed package root.
3031
+ * @returns The declaration target, or undefined when no branch resolves.
3032
+ */
3033
+ export function resolveDistributionDeclaration(
2937
3034
  entry: unknown,
2938
3035
  conditions: readonly string[],
2939
3036
  installed: string,
2940
3037
  ): string | undefined {
2941
- if (typeof entry === 'string') return targetToDeclaration(entry, installed)
2942
- if (isList(entry)) {
3038
+ if (typeof entry === 'string') return targetToDistributionDeclaration(entry, installed)
3039
+ if (isDistributionList(entry)) {
2943
3040
  for (const member of entry) {
2944
- const resolved = resolveDeclaration(member, conditions, installed)
3041
+ const resolved = resolveDistributionDeclaration(member, conditions, installed)
2945
3042
  if (resolved !== undefined) return resolved
2946
3043
  }
2947
3044
  return undefined
2948
3045
  }
2949
- if (!isRecord(entry)) return undefined
3046
+ if (!isDistributionRecord(entry)) return undefined
2950
3047
  for (const [condition, nested] of Object.entries(entry)) {
2951
3048
  if (condition !== 'default' && !conditions.includes(condition)) continue
2952
- const resolved = resolveDeclaration(nested, conditions, installed)
3049
+ const resolved = resolveDistributionDeclaration(nested, conditions, installed)
2953
3050
  if (resolved !== undefined) return resolved
2954
3051
  }
2955
3052
  return undefined
@@ -2958,7 +3055,14 @@ function resolveDeclaration(
2958
3055
  // The nearest package scope that decides a \`.d.ts\` declaration's module format. A
2959
3056
  // physical nested manifest starts a scope even when it omits \`type\` or cannot be
2960
3057
  // parsed. A directory at that spelling is not a manifest, so the walk continues.
2961
- function readPackageType(installed: string, target: string): unknown {
3058
+ /**
3059
+ * Reads the nearest package scope for a target inside an installed package.
3060
+ *
3061
+ * @param installed - The installed package root.
3062
+ * @param target - The package-relative target.
3063
+ * @returns The manifest type, or undefined when the nearest scope omits it or cannot be read.
3064
+ */
3065
+ export function readDistributionPackageType(installed: string, target: string): unknown {
2962
3066
  // One \`dirname\` per path segment reaches the root from any path. The walk ends at the
2963
3067
  // installed package, so no scope above it is consulted.
2964
3068
  const start = dirname(join(installed, target))
@@ -2968,57 +3072,91 @@ function readPackageType(installed: string, target: string): unknown {
2968
3072
  const end = chain.indexOf(installed)
2969
3073
  const scope = chain
2970
3074
  .slice(0, end === -1 ? chain.length : end + 1)
2971
- .find((directory) => matchesFile(join(directory, 'package.json')))
3075
+ .find((directory) => matchesDistributionFile(join(directory, 'package.json')))
2972
3076
  if (scope === undefined) return undefined
2973
3077
  try {
2974
- const manifest = readJson(join(scope, 'package.json'))
2975
- return isRecord(manifest) ? manifest.type : undefined
3078
+ const manifest = readDistributionJSON(join(scope, 'package.json'))
3079
+ return isDistributionRecord(manifest) ? manifest.type : undefined
2976
3080
  } catch {
2977
3081
  return undefined
2978
3082
  }
2979
3083
  }
2980
3084
 
2981
- function resolvesBrowser(entry: unknown): boolean {
2982
- const module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2983
- if (module !== undefined && module.startsWith(BROWSER_OUTPUT)) return true
3085
+ /**
3086
+ * Checks whether an entry publishes a distinct or conventional browser target.
3087
+ *
3088
+ * @param entry - The exports-map entry.
3089
+ * @returns True if the entry requires a browser drive; false otherwise.
3090
+ */
3091
+ export function resolvesDistributionBrowser(entry: unknown): boolean {
3092
+ const module = resolveDistributionTarget(entry, DISTRIBUTION_RUNTIME_CONDITIONS.browser)
3093
+ if (module !== undefined && module.startsWith(DISTRIBUTION_BROWSER_OUTPUT)) return true
2984
3094
  if (module === undefined) return false
2985
- const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
2986
- const required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
3095
+ const imported = resolveDistributionTarget(entry, DISTRIBUTION_RUNTIME_CONDITIONS.module)
3096
+ const required = resolveDistributionTarget(entry, DISTRIBUTION_RUNTIME_CONDITIONS.commonjs)
2987
3097
  return module !== imported && module !== required
2988
3098
  }
2989
3099
 
2990
3100
  // Whether the target selected by Node's CommonJS conditions is a module require can
2991
3101
  // load. A JavaScript target takes its own nearest package scope. Native addons and
2992
3102
  // extensionless targets have their own CommonJS handlers.
2993
- function resolvesCommonJS(entry: unknown, installed: string): boolean {
2994
- const target = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
3103
+ /**
3104
+ * Checks whether Node require can load an entry selected by its conditions.
3105
+ *
3106
+ * @param entry - The exports-map entry.
3107
+ * @param installed - The installed package root.
3108
+ * @returns True if the target has a CommonJS-compatible format; false otherwise.
3109
+ */
3110
+ export function resolvesDistributionCommonJS(entry: unknown, installed: string): boolean {
3111
+ const target = resolveDistributionTarget(entry, DISTRIBUTION_RUNTIME_CONDITIONS.commonjs)
2995
3112
  if (target === undefined) return false
2996
3113
  const name = target.slice(target.lastIndexOf('/') + 1)
2997
3114
  if (name.endsWith('.cjs')) return true
2998
3115
  if (name.endsWith('.mjs')) return false
2999
3116
  if (name.endsWith('.node')) return true
3000
3117
  if (!name.includes('.')) return true
3001
- return name.endsWith('.js') && readPackageType(installed, target) !== 'module'
3118
+ return name.endsWith('.js') && readDistributionPackageType(installed, target) !== 'module'
3002
3119
  }
3003
3120
 
3004
3121
  // Whether the declaration selected by a typed CommonJS consumer admits that entry.
3005
3122
  // A \`.d.cts\` declaration admits and a \`.d.mts\` declaration refuses. A \`.d.ts\`
3006
3123
  // declaration takes its own nearest package scope.
3007
- function declaresCommonJS(entry: unknown, installed: string): boolean {
3008
- const declaration = resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed)
3124
+ /**
3125
+ * Checks whether an entry resolves a CommonJS-compatible declaration.
3126
+ *
3127
+ * @param entry - The exports-map entry.
3128
+ * @param installed - The installed package root.
3129
+ * @returns True if the selected declaration admits CommonJS consumption; false otherwise.
3130
+ */
3131
+ export function declaresDistributionCommonJS(entry: unknown, installed: string): boolean {
3132
+ const declaration = resolveDistributionDeclaration(
3133
+ entry,
3134
+ DISTRIBUTION_DECLARATION_CONDITIONS.commonjs,
3135
+ installed,
3136
+ )
3009
3137
  if (declaration === undefined) return false
3010
3138
  if (declaration.endsWith('.d.cts')) return true
3011
3139
  if (declaration.endsWith('.d.mts')) return false
3012
- return declaration.endsWith('.d.ts') && readPackageType(installed, declaration) !== 'module'
3140
+ return (
3141
+ declaration.endsWith('.d.ts') &&
3142
+ readDistributionPackageType(installed, declaration) !== 'module'
3143
+ )
3013
3144
  }
3014
3145
 
3015
3146
  // Every target an entry names under any condition. A fallback list omits members
3016
3147
  // Node rejects during package-target validation, because no reader can take them.
3017
- function collectTargets(entry: unknown): readonly string[] {
3148
+ /**
3149
+ * Collects the targets an entry publishes under every condition.
3150
+ *
3151
+ * @param entry - The exports-map entry.
3152
+ * @returns The targets, excluding invalid members of fallback arrays.
3153
+ */
3154
+ export function collectDistributionTargets(entry: unknown): readonly string[] {
3018
3155
  if (typeof entry === 'string') return [entry]
3019
- if (isList(entry)) return entry.flatMap(collectTargets).filter(isPackageTarget)
3020
- if (!isRecord(entry)) return []
3021
- return Object.values(entry).flatMap((nested) => collectTargets(nested))
3156
+ if (isDistributionList(entry))
3157
+ return entry.flatMap(collectDistributionTargets).filter(matchesDistributionPackageTarget)
3158
+ if (!isDistributionRecord(entry)) return []
3159
+ return Object.values(entry).flatMap((nested) => collectDistributionTargets(nested))
3022
3160
  }
3023
3161
 
3024
3162
  // Whether a target is a file a runtime loads for its names, which is what a
@@ -3030,24 +3168,43 @@ function collectTargets(entry: unknown): readonly string[] {
3030
3168
  // \`"./package.json"\` manifest pointer, and a declaration alike.
3031
3169
  // The cost is an extensionless file published for a reader, such as a \`LICENSE\`:
3032
3170
  // that target reports undeclared until it is given an extension or a declaration.
3033
- function isModule(target: string): boolean {
3171
+ /**
3172
+ * Checks whether a target names a runtime module rather than an asset.
3173
+ *
3174
+ * @param target - The package-relative target.
3175
+ * @returns True if the target has a module extension or no extension; false otherwise.
3176
+ */
3177
+ export function matchesDistributionModule(target: string): boolean {
3034
3178
  const name = target.slice(target.lastIndexOf('/') + 1)
3035
3179
  const dot = name.lastIndexOf('.')
3036
- if (name.endsWith(ADDON_EXTENSION)) return true
3037
- return dot === -1 || MODULE_EXTENSIONS.includes(name.slice(dot))
3180
+ if (name.endsWith(DISTRIBUTION_ADDON_EXTENSION)) return true
3181
+ return dot === -1 || DISTRIBUTION_MODULE_EXTENSIONS.includes(name.slice(dot))
3038
3182
  }
3039
3183
 
3040
3184
  // Whether a resolved target is a declaration rather than the JavaScript a
3041
3185
  // \`default\` branch answers with when the entry declares no \`types\` condition.
3042
- function isDeclaration(target: string): boolean {
3043
- return DECLARATION_EXTENSIONS.some((extension) => target.endsWith(extension))
3186
+ /**
3187
+ * Checks whether a target names a TypeScript declaration.
3188
+ *
3189
+ * @param target - The package-relative target.
3190
+ * @returns True if the target has a declaration extension; false otherwise.
3191
+ */
3192
+ export function matchesDistributionDeclaration(target: string): boolean {
3193
+ return DISTRIBUTION_DECLARATION_EXTENSIONS.some((extension) => target.endsWith(extension))
3044
3194
  }
3045
3195
 
3046
3196
  // The declarations the Node module, Node CommonJS, and browser drives compare
3047
3197
  // against. Each field uses the conditions of the TypeScript consumer paired with
3048
3198
  // that runtime. A JavaScript target resolves through TypeScript's adjacent
3049
3199
  // declaration substitution rather than standing in for the declaration itself.
3050
- function readDeclaration(
3200
+ /**
3201
+ * Reads the declarations paired with Node and browser consumers.
3202
+ *
3203
+ * @param entry - The exports-map entry.
3204
+ * @param installed - The installed package root.
3205
+ * @returns The declaration selected by each consumer format.
3206
+ */
3207
+ export function readDistributionDeclaration(
3051
3208
  entry: unknown,
3052
3209
  installed: string,
3053
3210
  ): {
@@ -3056,17 +3213,39 @@ function readDeclaration(
3056
3213
  readonly browser: string | undefined
3057
3214
  } {
3058
3215
  return {
3059
- module: resolveDeclaration(entry, DECLARATION_CONDITIONS.module, installed),
3060
- commonjs: resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed),
3061
- browser: resolveDeclaration(entry, DECLARATION_CONDITIONS.browser, installed),
3216
+ module: resolveDistributionDeclaration(
3217
+ entry,
3218
+ DISTRIBUTION_DECLARATION_CONDITIONS.module,
3219
+ installed,
3220
+ ),
3221
+ commonjs: resolveDistributionDeclaration(
3222
+ entry,
3223
+ DISTRIBUTION_DECLARATION_CONDITIONS.commonjs,
3224
+ installed,
3225
+ ),
3226
+ browser: resolveDistributionDeclaration(
3227
+ entry,
3228
+ DISTRIBUTION_DECLARATION_CONDITIONS.browser,
3229
+ installed,
3230
+ ),
3062
3231
  }
3063
3232
  }
3064
3233
 
3065
3234
  // The entries one compile driver can resolve under its own conditions.
3066
- function selectEntries(entries: readonly Entry[], conditions: readonly string[]): readonly Entry[] {
3235
+ /**
3236
+ * Filters entries to those a consumer can resolve.
3237
+ *
3238
+ * @param entries - The installed entries.
3239
+ * @param conditions - The active consumer conditions.
3240
+ * @returns The matching entries in their original order.
3241
+ */
3242
+ export function filterDistributionEntries(
3243
+ entries: readonly DistributionEntry[],
3244
+ conditions: readonly string[],
3245
+ ): readonly DistributionEntry[] {
3067
3246
  return entries.filter(
3068
3247
  (entry) =>
3069
- resolveTarget(entry.mapping, conditions) !== undefined &&
3248
+ resolveDistributionTarget(entry.mapping, conditions) !== undefined &&
3070
3249
  (!conditions.includes('require') || entry.loadable),
3071
3250
  )
3072
3251
  }
@@ -3074,22 +3253,42 @@ function selectEntries(entries: readonly Entry[], conditions: readonly string[])
3074
3253
  // The compile drivers whose conditions reach one entry under one importing format.
3075
3254
  // A driver that resolves no target for that entry compiles nothing, so a consumer
3076
3255
  // written under it would report a resolution failure this package never made.
3077
- function selectDrivers(entry: Entry, format: Format): readonly Resolution[] {
3078
- return RESOLUTIONS.filter(
3079
- (driver) => selectEntries([entry], driver.conditions[format]).length > 0,
3256
+ /**
3257
+ * Filters compiler resolutions to those that can read an entry.
3258
+ *
3259
+ * @param entry - The installed entry.
3260
+ * @param format - The consumer module format.
3261
+ * @returns The matching compiler resolutions.
3262
+ */
3263
+ export function filterDistributionResolutions(
3264
+ entry: DistributionEntry,
3265
+ format: DistributionFormat,
3266
+ ): readonly DistributionResolution[] {
3267
+ return DISTRIBUTION_RESOLUTIONS.filter(
3268
+ (driver) => filterDistributionEntries([entry], driver.conditions[format]).length > 0,
3080
3269
  )
3081
3270
  }
3082
3271
 
3083
3272
  // Requirable entries that declare CommonJS support but a typed CommonJS consumer
3084
3273
  // cannot compile against. A default branch resolving under the require condition
3085
3274
  // set makes no CommonJS claim.
3086
- function selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {
3275
+ /**
3276
+ * Filters entries whose require target lacks a compatible declaration.
3277
+ *
3278
+ * @param entries - The installed entries.
3279
+ * @param installed - The installed package root.
3280
+ * @returns The entries a CommonJS consumer cannot typecheck.
3281
+ */
3282
+ export function filterDistributionUntypable(
3283
+ entries: readonly DistributionEntry[],
3284
+ installed: string,
3285
+ ): readonly DistributionEntry[] {
3087
3286
  return entries.filter(
3088
3287
  (entry) =>
3089
3288
  entry.requirable &&
3090
- isRecord(entry.mapping) &&
3289
+ isDistributionRecord(entry.mapping) &&
3091
3290
  Object.hasOwn(entry.mapping, 'require') &&
3092
- !declaresCommonJS(entry.mapping, installed),
3291
+ !declaresDistributionCommonJS(entry.mapping, installed),
3093
3292
  )
3094
3293
  }
3095
3294
 
@@ -3097,20 +3296,30 @@ function selectUntypable(entries: readonly Entry[], installed: string): readonly
3097
3296
  // installed entry that consumer imports, the file extension fixing its importing
3098
3297
  // format, the names a real runtime published off it, and the driver whose scratch
3099
3298
  // project compiles it.
3100
- interface Surface {
3101
- readonly entry: Entry
3299
+ /** Describes the runtime names and declaration resolution compared for one entry. */
3300
+ export interface DistributionSurface {
3301
+ readonly entry: DistributionEntry
3102
3302
  readonly extension: string
3103
3303
  readonly published: readonly string[]
3104
- readonly driver: Resolution
3304
+ readonly driver: DistributionResolution
3105
3305
  }
3106
3306
 
3107
3307
  // A scratch project over named consumer modules, written beside them so their own
3108
3308
  // resolution reaches the installed package. Nothing is emitted and no ambient types
3109
3309
  // are pulled in, so what the check reads is the installed declarations alone.
3110
- function writeProject(
3111
- stage: Stage,
3310
+ /**
3311
+ * Writes a compiler project for a consumer probe.
3312
+ *
3313
+ * @param stage - The installed consumer.
3314
+ * @param name - The project filename suffix.
3315
+ * @param driver - The compiler resolution.
3316
+ * @param files - The consumer source files.
3317
+ * @returns The absolute project path.
3318
+ */
3319
+ export function writeDistributionProject(
3320
+ stage: DistributionStage,
3112
3321
  name: string,
3113
- driver: Resolution,
3322
+ driver: DistributionResolution,
3114
3323
  files: readonly string[],
3115
3324
  ): string {
3116
3325
  const path = join(stage.consumer, \`tsconfig.\${name}.json\`)
@@ -3126,7 +3335,7 @@ function writeProject(
3126
3335
  },
3127
3336
  files: [...files],
3128
3337
  }
3129
- writeFile(path, \`\${JSON.stringify(project, undefined, '\\t')}\\n\`)
3338
+ writeDistributionFile(path, \`\${JSON.stringify(project, undefined, '\\t')}\\n\`)
3130
3339
  return path
3131
3340
  }
3132
3341
 
@@ -3137,8 +3346,21 @@ function writeProject(
3137
3346
  // naming the scratch project rather than a consumer module, and anything at all on
3138
3347
  // the error stream are faults of this proof rather than of the package under proof,
3139
3348
  // so each is raised where it happens instead of counted against the package.
3140
- function checkProject(stage: Stage, project: string): readonly string[] {
3141
- const result = runNode([TSC, '--noEmit', '--pretty', 'false', '-p', project], stage.consumer)
3349
+ /**
3350
+ * Checks a consumer project with the installed TypeScript compiler.
3351
+ *
3352
+ * @param stage - The installed consumer.
3353
+ * @param project - The compiler project path.
3354
+ * @returns The located compiler diagnostics. Throws on a tool or project failure.
3355
+ */
3356
+ export function checkDistributionProject(
3357
+ stage: DistributionStage,
3358
+ project: string,
3359
+ ): readonly string[] {
3360
+ const result = runDistributionNode(
3361
+ [DISTRIBUTION_COMPILER, '--noEmit', '--pretty', 'false', '-p', project],
3362
+ stage.consumer,
3363
+ )
3142
3364
  const refused = \`\${result.stderr ?? ''}\`.trim()
3143
3365
  if (refused.length > 0) {
3144
3366
  throw new Error(\`The consumer compiler wrote \${refused} to its error stream\`)
@@ -3150,7 +3372,7 @@ function checkProject(stage: Stage, project: string): readonly string[] {
3150
3372
  const heads = lines.flatMap((line, index) => (index === 0 || !/^\\s/u.test(line) ? [index] : []))
3151
3373
  const reported = heads.map((head, position) => {
3152
3374
  const line = lines[head] ?? ''
3153
- const located = DIAGNOSTIC_PATTERN.exec(line)?.[1]
3375
+ const located = DISTRIBUTION_DIAGNOSTIC_PATTERN.exec(line)?.[1]
3154
3376
  if (located === undefined) {
3155
3377
  throw new Error(\`The consumer compiler reported \${line}, which names no location\`)
3156
3378
  }
@@ -3163,7 +3385,7 @@ function checkProject(stage: Stage, project: string): readonly string[] {
3163
3385
  ].join(' ')
3164
3386
  })
3165
3387
  if (reported.length === 0 && result.status !== 0) {
3166
- throw new Error(\`The consumer compiler refused the project: \${readOutput(result)}\`)
3388
+ throw new Error(\`The consumer compiler refused the project: \${readDistributionOutput(result)}\`)
3167
3389
  }
3168
3390
  return reported
3169
3391
  }
@@ -3178,26 +3400,47 @@ function checkProject(stage: Stage, project: string): readonly string[] {
3178
3400
  // as a type alone, land on \`surfaced\`: a variable widens into the type
3179
3401
  // \`declared\` annotates, and only \`surfaced\` reads the literal's own keys back.
3180
3402
  // Each names the member it is about, so the failure says which export moved.
3181
- function checkSurface(stage: Stage, surface: Surface): readonly string[] {
3403
+ /**
3404
+ * Checks runtime export names against their installed declarations.
3405
+ *
3406
+ * @param stage - The installed consumer.
3407
+ * @param surface - The publication and declaration pair.
3408
+ * @returns The diagnostics labelled by compiler resolution.
3409
+ */
3410
+ export function checkDistributionSurface(
3411
+ stage: DistributionStage,
3412
+ surface: DistributionSurface,
3413
+ ): readonly string[] {
3182
3414
  const slug = surface.entry.subpath.replaceAll(/[^\\w]+/gu, '-')
3183
3415
  const name = \`surface.\${surface.driver.label}\${slug}.\${surface.extension}\`
3184
3416
  const module = \`\${name}\`
3185
3417
  const keys = surface.published.map((key) => \`\${JSON.stringify(key)}: true\`).join(', ')
3186
- writeFile(
3418
+ writeDistributionFile(
3187
3419
  join(stage.consumer, module),
3188
3420
  \`import * as entry from \${JSON.stringify(surface.entry.specifier)}
3189
- const published = {\${keys.length === 0 ? '' : \` \${keys} \`}} as const
3190
- const declared: Record<keyof typeof entry, true> = published
3191
- const surfaced: Record<keyof typeof published, true> = declared
3421
+ export const published = {\${keys.length === 0 ? '' : \` \${keys} \`}} as const
3422
+ export const declared: Record<keyof typeof entry, true> = published
3423
+ export const surfaced: Record<keyof typeof published, true> = declared
3192
3424
  \`,
3193
3425
  )
3194
- const project = writeProject(stage, name, surface.driver, [\`./\${module}\`])
3195
- return checkProject(stage, project).map((line) => \`\${surface.driver.label}: \${line}\`)
3426
+ const project = writeDistributionProject(stage, name, surface.driver, [\`./\${module}\`])
3427
+ return checkDistributionProject(stage, project).map((line) => \`\${surface.driver.label}: \${line}\`)
3196
3428
  }
3197
3429
 
3198
3430
  // One consumer module importing every installed entry, written where its own
3199
3431
  // resolution finds the installed package.
3200
- function writeConsumerProbe(stage: Stage, path: string, specifiers: readonly string[]): void {
3432
+ /**
3433
+ * Writes a consumer importing the requested public specifiers.
3434
+ *
3435
+ * @param stage - The installed consumer.
3436
+ * @param path - The consumer-relative source path.
3437
+ * @param specifiers - The public imports to resolve.
3438
+ */
3439
+ export function writeDistributionConsumerProbe(
3440
+ stage: DistributionStage,
3441
+ path: string,
3442
+ specifiers: readonly string[],
3443
+ ): void {
3201
3444
  const modules = specifiers.map((specifier, index) => ({
3202
3445
  binding: \`entry\${String(index)}\`,
3203
3446
  specifier,
@@ -3207,112 +3450,94 @@ function writeConsumerProbe(stage: Stage, path: string, specifiers: readonly str
3207
3450
  )
3208
3451
  const names = modules.map(({ binding }) => binding)
3209
3452
  const source = \`\${bindings.join('\\n')}\\nexport const surface = [\${names.join(', ')}]\\n\`
3210
- writeFile(join(stage.consumer, path), source)
3453
+ writeDistributionFile(join(stage.consumer, path), source)
3211
3454
  }
3212
3455
 
3213
3456
  // The runtime key set a real process reads off one installed entry under one
3214
3457
  // condition. The driver is a file rather than an \`--eval\` string, so the specifier
3215
3458
  // travels as an argument and nothing needs escaping.
3216
- function driveRuntime(stage: Stage, specifier: string, driver: string): readonly string[] {
3217
- const result = runNode([join(stage.consumer, driver), specifier], stage.consumer)
3459
+ /**
3460
+ * Reads the names a real Node process loads from an installed entry.
3461
+ *
3462
+ * @param stage - The installed consumer.
3463
+ * @param specifier - The public package specifier.
3464
+ * @param driver - The consumer driver filename.
3465
+ * @returns The exported names. Throws when loading or decoding fails.
3466
+ */
3467
+ export function driveDistributionRuntime(
3468
+ stage: DistributionStage,
3469
+ specifier: string,
3470
+ driver: string,
3471
+ ): readonly string[] {
3472
+ const result = runDistributionNode([join(stage.consumer, driver), specifier], stage.consumer)
3218
3473
  if (result.status !== 0) {
3219
- throw new Error(\`Loading \${specifier} from the consumer failed: \${readOutput(result)}\`)
3474
+ throw new Error(
3475
+ \`Loading \${specifier} from the consumer failed: \${readDistributionOutput(result)}\`,
3476
+ )
3220
3477
  }
3221
3478
  const published: unknown = JSON.parse(result.stdout)
3222
- if (!isNames(published)) throw new Error(\`The driver printed no name list for \${specifier}\`)
3479
+ if (!isDistributionNames(published))
3480
+ throw new Error(\`The driver printed no name list for \${specifier}\`)
3223
3481
  return published
3224
3482
  }
3225
3483
  {{helpers}}
3226
3484
  // Pack this workspace, install the archive into an isolated consumer, and read the
3227
3485
  // published surface back off the installed tree. Every later claim reads this
3228
3486
  // result, so a failure here is raised where it happens rather than once per entry.
3229
- function buildStage(scratch: string): Stage {
3487
+ /**
3488
+ * Packs the workspace and installs its archive into an isolated consumer.
3489
+ *
3490
+ * @param scratch - The owned scratch root.
3491
+ * @returns The installed consumer and classified exports.
3492
+ * @throws Thrown when packing or installation fails.
3493
+ */
3494
+ export function buildDistributionStage(scratch: string): DistributionStage {
3230
3495
  const packed = join(scratch, 'packed')
3231
3496
  const consumer = join(scratch, 'consumer')
3232
3497
  mkdirSync(packed, { recursive: true })
3233
- const pack = runNpm(['pack', '--ignore-scripts', '--pack-destination', packed], ROOT, scratch)
3234
- if (pack.status !== 0) throw new Error(\`npm pack refused this workspace: \${readOutput(pack)}\`)
3498
+ const pack = runDistributionNpm(
3499
+ ['pack', '--ignore-scripts', '--pack-destination', packed],
3500
+ DISTRIBUTION_ROOT,
3501
+ scratch,
3502
+ )
3503
+ if (pack.status !== 0)
3504
+ throw new Error(\`npm pack refused this workspace: \${readDistributionOutput(pack)}\`)
3235
3505
  const archives = readdirSync(packed).filter((name) => name.endsWith('.tgz'))
3236
3506
  const archive = archives[0]
3237
3507
  if (archives.length !== 1 || archive === undefined) {
3238
3508
  throw new Error(\`npm pack wrote no single archive: \${archives.join(', ')}\`)
3239
3509
  }
3240
- writeFile(join(consumer, 'package.json'), CONSUMER_MANIFEST)
3241
- writeFile(join(consumer, ESM_DRIVER), ESM_DRIVER_SOURCE)
3242
- writeFile(join(consumer, CJS_DRIVER), CJS_DRIVER_SOURCE)
3243
- const install = runNpm(
3510
+ writeDistributionFile(join(consumer, 'package.json'), DISTRIBUTION_CONSUMER_MANIFEST)
3511
+ writeDistributionFile(join(consumer, DISTRIBUTION_ESM_DRIVER), DISTRIBUTION_ESM_DRIVER_SOURCE)
3512
+ writeDistributionFile(join(consumer, DISTRIBUTION_CJS_DRIVER), DISTRIBUTION_CJS_DRIVER_SOURCE)
3513
+ const install = runDistributionNpm(
3244
3514
  ['install', '--ignore-scripts', '--no-audit', '--no-fund', join(packed, archive)],
3245
3515
  consumer,
3246
3516
  scratch,
3247
3517
  )
3248
3518
  if (install.status !== 0) {
3249
- throw new Error(\`Installing the packed archive failed: \${readOutput(install)}\`)
3519
+ throw new Error(\`Installing the packed archive failed: \${readDistributionOutput(install)}\`)
3250
3520
  }
3251
- const name = readManifestName(join(ROOT, 'package.json'))
3521
+ const name = readDistributionManifestName(join(DISTRIBUTION_ROOT, 'package.json'))
3252
3522
  const installed = join(consumer, 'node_modules', ...name.split('/'))
3253
- const manifest = readJson(join(installed, 'package.json'))
3254
- if (!isRecord(manifest) || !isRecord(manifest.exports)) {
3523
+ const manifest = readDistributionJSON(join(installed, 'package.json'))
3524
+ if (!isDistributionRecord(manifest) || !isDistributionRecord(manifest.exports)) {
3255
3525
  throw new Error('The installed manifest publishes no exports map')
3256
3526
  }
3257
- const readings = Object.entries(manifest.exports).map(([subpath, mapping]) => {
3258
- const files = collectTargets(mapping)
3259
- const declaration = readDeclaration(mapping, installed)
3260
- // A subpath resolving no declaration is partitioned rather than dropped. It is a
3261
- // defect when a runtime loads one of its targets for names, because a consumer
3262
- // importing it compiles against nothing under \`node16\`. It is an excluded
3263
- // publication otherwise: the \`"./package.json"\` manifest pointer and a stylesheet
3264
- // are published for a reader rather than an importer.
3265
- if (
3266
- declaration.module === undefined &&
3267
- declaration.commonjs === undefined &&
3268
- declaration.browser === undefined
3269
- ) {
3270
- return { subpath, files, entry: undefined }
3271
- }
3272
- const importTarget = resolveTarget(mapping, RUNTIME_CONDITIONS.module)
3273
- const requireTarget = resolveTarget(mapping, RUNTIME_CONDITIONS.commonjs)
3274
- const browserTarget = resolveTarget(mapping, RUNTIME_CONDITIONS.browser)
3275
- const browsable = resolvesBrowser(mapping)
3276
- const shadowed = browsable && requireTarget === browserTarget
3277
- const requirable = requireTarget !== undefined && !shadowed
3278
- const loadable = requirable && resolvesCommonJS(mapping, installed)
3279
- const entry: Entry = {
3280
- subpath,
3281
- specifier: subpath === '.' ? name : \`\${name}\${subpath.slice(1)}\`,
3282
- mapping,
3283
- declaration: {
3284
- importable: declaration.module !== undefined,
3285
- requirable: declaration.commonjs !== undefined,
3286
- browsable: declaration.browser !== undefined,
3287
- },
3288
- browsable,
3289
- importable: importTarget !== undefined && !(browsable && importTarget === browserTarget),
3290
- requirable,
3291
- loadable,
3292
- }
3293
- return { subpath, files, entry }
3294
- })
3295
- const entries = readings.flatMap(({ entry }) => (entry === undefined ? [] : [entry]))
3296
- const targets = readings.flatMap(({ files }) => files)
3297
- const subpaths = readings.map(({ subpath }) => subpath)
3298
- const undeclared = readings
3299
- .filter(({ entry, files }) => entry === undefined && files.some(isModule))
3300
- .map(({ subpath }) => subpath)
3301
- const excluded = readings
3302
- .filter(({ entry, files }) => entry === undefined && !files.some(isModule))
3303
- .map(({ subpath }) => subpath)
3304
- return { consumer, installed, archives, entries, subpaths, undeclared, excluded, targets }
3527
+ return { consumer, installed, archives, ...classifyDistributionStage(manifest, installed, name) }
3305
3528
  }
3306
3529
 
3307
3530
  // One file's run: the scratch directory and the staged consumer, which is absent when the
3308
3531
  // registry could not be reached.
3309
- interface DistributionRun {
3532
+ /** Binds an owned scratch directory to its optional installed consumer. */
3533
+ export interface DistributionRun {
3310
3534
  readonly scratch: string
3311
- readonly stage: Stage | undefined
3535
+ readonly stage: DistributionStage | undefined
3312
3536
  }
3313
3537
 
3314
3538
  // The run the start hook binds once for this file and the stop hook releases.
3315
- class DistributionSuite {
3539
+ /** Owns the staged consumer shared by one distribution proof. */
3540
+ export class DistributionSuite implements DistributionSuiteInterface {
3316
3541
  #run: DistributionRun | undefined
3317
3542
 
3318
3543
  get scratch(): string {
@@ -3322,8 +3547,12 @@ class DistributionSuite {
3322
3547
  return this.#run.scratch
3323
3548
  }
3324
3549
 
3325
- get stage(): Stage | undefined {
3326
- return this.#run?.stage
3550
+ read(context: TestContext): DistributionStage {
3551
+ const stage = this.#run?.stage
3552
+ if (stage === undefined) {
3553
+ return context.skip('\`npm ping\` did not answer, so nothing was packed or installed')
3554
+ }
3555
+ return stage
3327
3556
  }
3328
3557
 
3329
3558
  start(): void {
@@ -3331,7 +3560,7 @@ class DistributionSuite {
3331
3560
  throw new Error('The distribution setup has already allocated its scratch directory')
3332
3561
  }
3333
3562
  const scratch = mkdtempSync(join(tmpdir(), 'distribution-'))
3334
- this.#run = { scratch, stage: openStage(scratch) }
3563
+ this.#run = { scratch, stage: openDistributionStage(scratch) }
3335
3564
  }
3336
3565
 
3337
3566
  destroy(): void {
@@ -3342,25 +3571,26 @@ class DistributionSuite {
3342
3571
  }
3343
3572
  }
3344
3573
 
3345
- const DISTRIBUTION_SUITE = new DistributionSuite()
3346
-
3347
- beforeAll(() => DISTRIBUTION_SUITE.start())
3348
-
3349
- afterAll(() => DISTRIBUTION_SUITE.destroy())
3350
-
3351
3574
  // Installing the packed archive resolves its own runtime dependencies, so an
3352
3575
  // unreachable registry leaves nothing to measure. Under release that is the gate
3353
3576
  // failing; anywhere else the suite skips and names the mechanism it wanted.
3354
- function openStage(scratch: string): Stage | undefined {
3577
+ /**
3578
+ * Opens an installed consumer after checking registry readiness.
3579
+ *
3580
+ * @param scratch - The owned scratch root.
3581
+ * @returns The installed consumer, or undefined when an ordinary run cannot reach the registry.
3582
+ * @throws Thrown in release mode or when staging fails, after removing scratch.
3583
+ */
3584
+ export function openDistributionStage(scratch: string): DistributionStage | undefined {
3355
3585
  try {
3356
3586
  mkdirSync(join(scratch, 'cache'), { recursive: true })
3357
- if (runNpm(PING, ROOT, scratch).status !== 0) {
3358
- if (!RELEASE) return undefined
3587
+ if (runDistributionNpm(DISTRIBUTION_PING, DISTRIBUTION_ROOT, scratch).status !== 0) {
3588
+ if (!DISTRIBUTION_RELEASE) return undefined
3359
3589
  throw new Error(
3360
3590
  'The release gate requires a reachable npm registry, and npm ping did not answer',
3361
3591
  )
3362
3592
  }
3363
- return buildStage(scratch)
3593
+ return buildDistributionStage(scratch)
3364
3594
  } catch (error) {
3365
3595
  rmSync(scratch, {
3366
3596
  force: true,
@@ -3372,200 +3602,690 @@ function openStage(scratch: string): Stage | undefined {
3372
3602
  }
3373
3603
  }
3374
3604
 
3375
- describe('distribution classifiers', () => {
3376
- it('classifies synthetic export mappings without a registry stage', () => {
3377
- const root = join(DISTRIBUTION_SUITE.scratch, 'classifiers')
3378
- writeFile(
3379
- join(root, 'package.json'),
3380
- JSON.stringify({
3381
- type: 'commonjs',
3382
- exports: {
3383
- condition: { browser: './b.js', default: './n.js' },
3384
- convention: { default: './dist/src/browser/index.js' },
3385
- universal: { default: './shared.js' },
3386
- 'import-shared': {
3387
- browser: './shared.mjs',
3388
- import: './shared.mjs',
3389
- default: './node.js',
3390
- },
3391
- 'require-shared': {
3392
- browser: './shared.cjs',
3393
- require: './shared.cjs',
3394
- default: './node.js',
3395
- },
3396
- node: { node: './node.js', default: './node.js' },
3397
- silent: { 'module-sync': './x.cjs', import: './x.mjs' },
3398
- module: { require: './x.mjs' },
3399
- 'nested-module': { require: './module/x.js' },
3400
- 'nested-commonjs': { require: './commonjs/x.js' },
3401
- esm: { import: './x.mjs' },
3402
- },
3403
- }),
3605
+ /**
3606
+ * Partitions an installed exports map into driven, undeclared, and excluded subpaths.
3607
+ *
3608
+ * @param manifest - The installed package manifest.
3609
+ * @param installed - The installed package root.
3610
+ * @param name - The published package name.
3611
+ * @returns The classified entries and complete target population.
3612
+ * @throws Thrown when the exports map is absent.
3613
+ */
3614
+ export function classifyDistributionStage(
3615
+ manifest: Readonly<Record<string, unknown>>,
3616
+ installed: string,
3617
+ name: string,
3618
+ ): DistributionClassification {
3619
+ if (!isDistributionRecord(manifest.exports))
3620
+ throw new Error('The installed manifest publishes no exports map')
3621
+ const readings = Object.entries(manifest.exports).map(([subpath, mapping]) => {
3622
+ const files = collectDistributionTargets(mapping)
3623
+ const declaration = readDistributionDeclaration(mapping, installed)
3624
+ // A subpath resolving no declaration is partitioned rather than dropped. It is a
3625
+ // defect when a runtime loads one of its targets for names, because a consumer
3626
+ // importing it compiles against nothing under \`node16\`. It is an excluded
3627
+ // publication otherwise: the \`"./package.json"\` manifest pointer and a stylesheet
3628
+ // are published for a reader rather than an importer.
3629
+ if (
3630
+ declaration.module === undefined &&
3631
+ declaration.commonjs === undefined &&
3632
+ declaration.browser === undefined
3633
+ ) {
3634
+ return { subpath, files, entry: undefined }
3635
+ }
3636
+ const importTarget = resolveDistributionTarget(mapping, DISTRIBUTION_RUNTIME_CONDITIONS.module)
3637
+ const requireTarget = resolveDistributionTarget(
3638
+ mapping,
3639
+ DISTRIBUTION_RUNTIME_CONDITIONS.commonjs,
3404
3640
  )
3405
- writeFile(join(root, 'module/package.json'), '{ "type": "module" }\\n')
3406
- writeFile(join(root, 'commonjs/package.json'), '{ "type": "commonjs" }\\n')
3407
- const manifest = readJson(join(root, 'package.json'))
3408
- if (!isRecord(manifest) || !isRecord(manifest.exports)) {
3409
- throw new Error('The classifier fixture declares no exports map')
3641
+ const browserTarget = resolveDistributionTarget(
3642
+ mapping,
3643
+ DISTRIBUTION_RUNTIME_CONDITIONS.browser,
3644
+ )
3645
+ const browsable = resolvesDistributionBrowser(mapping)
3646
+ const shadowed = browsable && requireTarget === browserTarget
3647
+ const requirable = requireTarget !== undefined && !shadowed
3648
+ const loadable = requirable && resolvesDistributionCommonJS(mapping, installed)
3649
+ const entry: DistributionEntry = {
3650
+ subpath,
3651
+ specifier: subpath === '.' ? name : \`\${name}\${subpath.slice(1)}\`,
3652
+ mapping,
3653
+ declaration: {
3654
+ importable: declaration.module !== undefined,
3655
+ requirable: declaration.commonjs !== undefined,
3656
+ browsable: declaration.browser !== undefined,
3657
+ },
3658
+ browsable,
3659
+ importable: importTarget !== undefined && !(browsable && importTarget === browserTarget),
3660
+ requirable,
3661
+ loadable,
3410
3662
  }
3411
- const mappings = manifest.exports
3412
- expect({
3413
- condition: resolvesBrowser(mappings.condition),
3414
- convention: resolvesBrowser(mappings.convention),
3415
- universal: resolvesBrowser(mappings.universal),
3416
- import: resolvesBrowser(mappings['import-shared']),
3417
- require: resolvesBrowser(mappings['require-shared']),
3418
- node: resolvesBrowser(mappings.node),
3419
- }).toStrictEqual({
3420
- condition: true,
3421
- convention: true,
3422
- universal: false,
3423
- import: false,
3424
- require: false,
3425
- node: false,
3426
- })
3427
- expect({
3428
- silent: resolvesCommonJS(mappings.silent, root),
3429
- module: resolvesCommonJS(mappings.module, root),
3430
- nestedModule: resolvesCommonJS(mappings['nested-module'], root),
3431
- nestedCommonJS: resolvesCommonJS(mappings['nested-commonjs'], root),
3432
- esm: resolvesCommonJS(mappings.esm, root),
3433
- }).toStrictEqual({
3434
- silent: true,
3435
- module: false,
3436
- nestedModule: false,
3437
- nestedCommonJS: true,
3438
- esm: false,
3439
- })
3663
+ return { subpath, files, entry }
3440
3664
  })
3441
- })
3665
+ const entries = readings.flatMap(({ entry }) => (entry === undefined ? [] : [entry]))
3666
+ const targets = readings.flatMap(({ files }) => files)
3667
+ const subpaths = readings.map(({ subpath }) => subpath)
3668
+ const undeclared = readings
3669
+ .filter(({ entry, files }) => entry === undefined && files.some(matchesDistributionModule))
3670
+ .map(({ subpath }) => subpath)
3671
+ const excluded = readings
3672
+ .filter(({ entry, files }) => entry === undefined && !files.some(matchesDistributionModule))
3673
+ .map(({ subpath }) => subpath)
3674
+ return { entries, subpaths, undeclared, excluded, targets }
3675
+ }
3442
3676
 
3443
- // The staged consumer, or a skip naming what the run could not reach. \`it.skipIf\`
3444
- // carries no reason, so the gate sits here where the test context can state one.
3445
- function requireStage(context: TestContext): Stage {
3446
- const staged = DISTRIBUTION_SUITE.stage
3447
- if (staged === undefined) {
3448
- return context.skip('\`npm ping\` did not answer, so nothing was packed or installed')
3449
- }
3450
- return staged
3677
+ /**
3678
+ * Creates the lifecycle boundary used by generated distribution proofs.
3679
+ * @returns The suite interface backed by an owned run.
3680
+ */
3681
+ export function createDistributionSuite(): DistributionSuiteInterface {
3682
+ return new DistributionSuite()
3451
3683
  }
3452
3684
 
3453
- describe('installed package consumer', () => {
3454
- it('packs one archive and installs it in isolation [requires the registry]', (context) => {
3455
- const stage = requireStage(context)
3456
- expect(stage.archives).toHaveLength(1)
3457
- expect(existsSync(join(stage.installed, 'package.json'))).toBe(true)
3458
- expect(stage.entries.length).toBeGreaterThan(0)
3685
+ /**
3686
+ * Collects physical files beneath an installed output directory.
3687
+ * @param directory - The output directory to walk.
3688
+ * @returns The installed files, or an empty list when the directory is absent.
3689
+ */
3690
+ export function collectDistributionFiles(directory: string): readonly string[] {
3691
+ if (!existsSync(directory)) return []
3692
+ return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
3693
+ const path = join(directory, entry.name)
3694
+ return entry.isDirectory() ? collectDistributionFiles(path) : entry.isFile() ? [path] : []
3459
3695
  })
3696
+ }
3460
3697
 
3461
- it('ships every relative target its exports map names [requires the registry]', (context) => {
3462
- const stage = requireStage(context)
3463
- const relative = stage.targets.filter((target) => target.startsWith('./'))
3464
- expect(relative).not.toStrictEqual([])
3465
- expect(relative.filter((target) => !existsSync(join(stage.installed, target)))).toStrictEqual(
3466
- [],
3467
- )
3698
+ /**
3699
+ * Reads the source environment owning an installed output path.
3700
+ * @param installed - The installed package root.
3701
+ * @param path - The output file.
3702
+ * @returns The source environment, or undefined outside a JavaScript face.
3703
+ */
3704
+ export function readDistributionFace(installed: string, path: string): string | undefined {
3705
+ const [output, axis, face] = relativePath(installed, path).split(sep)
3706
+ return output === 'dist'
3707
+ ? axis === 'src'
3708
+ ? face
3709
+ : axis === 'bin'
3710
+ ? 'bin'
3711
+ : undefined
3712
+ : undefined
3713
+ }
3714
+
3715
+ /**
3716
+ * Scans installed JavaScript maps for sources copied from another environment.
3717
+ * @param installed - The installed package root.
3718
+ * @returns The copied sources and unreadable maps.
3719
+ * @throws Thrown when a code-bearing output has no readable map of its own.
3720
+ */
3721
+ export function scanDistributionSourceMaps(installed: string): readonly string[] {
3722
+ const files = collectDistributionFiles(join(installed, 'dist')).filter(
3723
+ (path) => readDistributionFace(installed, path) !== undefined,
3724
+ )
3725
+ const outputs = files
3726
+ .filter((path) => /\\.(?:js|cjs|mjs)$/u.test(path))
3727
+ .map((path) => {
3728
+ const parsed = parseSync(path, readFileSync(path, 'utf8'))
3729
+ const code = parsed.program.body.some(
3730
+ (statement) =>
3731
+ statement.type !== 'EmptyStatement' &&
3732
+ !(
3733
+ statement.type === 'ExportNamedDeclaration' &&
3734
+ statement.declaration === null &&
3735
+ statement.specifiers.length === 0 &&
3736
+ statement.source === null
3737
+ ),
3738
+ )
3739
+ const reference = parsed.comments
3740
+ .flatMap((comment) => {
3741
+ const match = /^[@#]\\s*sourceMappingURL=(\\S+)\\s*$/u.exec(comment.value)
3742
+ return match?.[1] === undefined ? [] : [match[1]]
3743
+ })
3744
+ .at(-1)
3745
+ try {
3746
+ const inline = reference?.startsWith('data:') === true
3747
+ const target = inline
3748
+ ? path
3749
+ : reference === undefined
3750
+ ? \`\${path}.map\`
3751
+ : fileURLToPath(new URL(reference.replaceAll('\\\\', '/'), pathToFileURL(path)))
3752
+ return {
3753
+ path: target,
3754
+ face: readDistributionFace(installed, path),
3755
+ code,
3756
+ reference: inline ? reference : undefined,
3757
+ }
3758
+ } catch {
3759
+ throw new Error(\`Unreadable source maps for \${path}\`)
3760
+ }
3761
+ })
3762
+ const maps = [
3763
+ ...outputs.filter(
3764
+ ({ path, code, reference }) => code || reference !== undefined || existsSync(path),
3765
+ ),
3766
+ ...files
3767
+ .filter(
3768
+ (path) =>
3769
+ /\\.(?:js|cjs|mjs)\\.map$/u.test(path) && !outputs.some((output) => output.path === path),
3770
+ )
3771
+ .map((path) => ({
3772
+ path,
3773
+ face: readDistributionFace(installed, path),
3774
+ code: false,
3775
+ reference: undefined,
3776
+ })),
3777
+ ]
3778
+ if (maps.length === 0)
3779
+ console.info('No built JavaScript face holds code, so no source map is required')
3780
+ return maps.flatMap(({ path, face, code, reference }) => {
3781
+ const label = relativePath(installed, path).split(sep).join('/')
3782
+ try {
3783
+ const payload =
3784
+ reference === undefined
3785
+ ? undefined
3786
+ : /^data:application\\/json(?:;charset=[^;,]+)?(;base64)?,(.*)$/iu.exec(reference)
3787
+ const map: unknown =
3788
+ reference === undefined
3789
+ ? readDistributionJSON(path)
3790
+ : payload?.[2] === undefined
3791
+ ? undefined
3792
+ : JSON.parse(
3793
+ payload[1] === undefined
3794
+ ? decodeURIComponent(payload[2])
3795
+ : Buffer.from(payload[2], 'base64').toString('utf8'),
3796
+ )
3797
+ if (
3798
+ !isDistributionRecord(map) ||
3799
+ !isDistributionNames(map.sources) ||
3800
+ (map.sourceRoot !== undefined && typeof map.sourceRoot !== 'string')
3801
+ ) {
3802
+ throw new Error('Unreadable source map')
3803
+ }
3804
+ const root = typeof map.sourceRoot === 'string' ? map.sourceRoot : ''
3805
+ const directory = root.startsWith('file:')
3806
+ ? fileURLToPath(new URL(root))
3807
+ : resolve(dirname(path), root.replaceAll('\\\\', '/'))
3808
+ const sources = map.sources.map((source) =>
3809
+ relativePath(
3810
+ resolve(installed),
3811
+ source.startsWith('file:')
3812
+ ? fileURLToPath(new URL(source))
3813
+ : resolve(directory, source.replaceAll('\\\\', '/')),
3814
+ )
3815
+ .split(sep)
3816
+ .join('/'),
3817
+ )
3818
+ const copied = sources.filter(
3819
+ (source) => source.startsWith('src/') && !source.startsWith(\`src/\${face}/\`),
3820
+ )
3821
+ return [
3822
+ ...copied.map((source) => \`\${label}: \${source}\`),
3823
+ ...(sources.some((source) => source.startsWith(\`src/\${face}/\`))
3824
+ ? []
3825
+ : [\`\${label}: unreadable source map (no src/\${face}/ source)\`]),
3826
+ ]
3827
+ } catch {
3828
+ if (code) throw new Error(\`Unreadable source maps for \${path}\`)
3829
+ return [\`\${label}: unreadable source map\`]
3830
+ }
3468
3831
  })
3832
+ }
3469
3833
 
3470
- // Every published subpath is driven, excluded by name, or reported here. A dropped
3471
- // one leaves no trace: no runtime test, no declaration comparison, and no place in
3472
- // the resolution compile, so the run reports success for a subpath it never
3473
- // measured.
3474
- it('declares types for every module it publishes [requires the registry]', (context) => {
3475
- const stage = requireStage(context)
3476
- const partitioned = [
3477
- ...stage.entries.map((entry) => entry.subpath),
3478
- ...stage.undeclared,
3479
- ...stage.excluded,
3480
- ]
3481
- expect(stage.undeclared).toStrictEqual([])
3482
- expect(partitioned.sort()).toStrictEqual([...stage.subpaths].sort())
3483
- // A driven subpath answers a runtime condition. One resolving a declaration and
3484
- // no Node or browser target compiles for a consumer and throws when that consumer
3485
- // loads it. Each later drive retires itself for that entry, so this assertion names
3486
- // the subpath rather than counting it as driven.
3487
- const unreachable = stage.entries.filter(
3488
- (entry) => !entry.importable && !entry.requirable && !entry.browsable,
3489
- )
3490
- expect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])
3491
- const untypable = selectUntypable(stage.entries, stage.installed)
3492
- expect(untypable.map((entry) => entry.subpath)).toStrictEqual([])
3834
+ /**
3835
+ * Reads top-level names declared by a roll-up, including unexported declarations.
3836
+ * @param path - The installed declaration file.
3837
+ * @returns The declared names. Refuses syntax errors and unreadable binding patterns.
3838
+ */
3839
+ export function readDistributionNames(path: string): readonly string[] {
3840
+ const source = parseSync(path, readFileSync(path, 'utf8'))
3841
+ if (source.errors.length > 0) throw new Error(\`Unreadable declarations at \${path}\`)
3842
+ return source.program.body.flatMap((statement) => {
3843
+ const declaration =
3844
+ statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration'
3845
+ ? statement.declaration
3846
+ : statement
3847
+ if (declaration === null) return []
3848
+ if (declaration.type === 'VariableDeclaration') {
3849
+ return declaration.declarations.map((binding) => {
3850
+ if (binding.id.type !== 'Identifier')
3851
+ throw new Error(\`Unreadable declaration binding at \${path}\`)
3852
+ return binding.id.name
3853
+ })
3854
+ }
3855
+ if (
3856
+ declaration.type === 'ClassDeclaration' ||
3857
+ declaration.type === 'FunctionDeclaration' ||
3858
+ declaration.type === 'TSDeclareFunction' ||
3859
+ declaration.type === 'TSInterfaceDeclaration' ||
3860
+ declaration.type === 'TSTypeAliasDeclaration' ||
3861
+ declaration.type === 'TSEnumDeclaration' ||
3862
+ declaration.type === 'TSModuleDeclaration'
3863
+ ) {
3864
+ return declaration.id?.type === 'Identifier' ? [declaration.id.name] : []
3865
+ }
3866
+ return []
3493
3867
  })
3868
+ }
3494
3869
 
3495
- it('refuses a subpath its exports map does not name [requires the registry]', (context) => {
3496
- const stage = requireStage(context)
3497
- const name = readManifestName(join(stage.installed, 'package.json'))
3498
- const driver = join(stage.consumer, ESM_DRIVER)
3499
- const result = runNode([driver, \`\${name}\${ABSENT_SUBPATH}\`], stage.consumer)
3500
- expect(result.status).not.toBe(0)
3501
- expect(readOutput(result)).toContain('ERR_PACKAGE_PATH_NOT_EXPORTED')
3870
+ /**
3871
+ * Checks whether a source entry exports names, following local star exports.
3872
+ * @param path - The source entry to inspect.
3873
+ * @param ancestors - The entries already visited on this export path.
3874
+ * @returns True if the entry exports a name; false otherwise.
3875
+ */
3876
+ export function matchesDistributionSourceExports(
3877
+ path: string,
3878
+ ancestors: readonly string[] = [],
3879
+ ): boolean {
3880
+ if (ancestors.includes(path)) return false
3881
+ const source = parseSync(path, readFileSync(path, 'utf8'))
3882
+ if (source.errors.length > 0) throw new Error(\`Unreadable source exports at \${path}\`)
3883
+ return source.program.body.some((statement) => {
3884
+ if (statement.type === 'ExportDefaultDeclaration') return ancestors.length === 0
3885
+ if (statement.type === 'ExportNamedDeclaration')
3886
+ return (
3887
+ statement.declaration !== null ||
3888
+ statement.specifiers.some((specifier) => {
3889
+ const name =
3890
+ specifier.exported.type === 'Identifier'
3891
+ ? specifier.exported.name
3892
+ : specifier.exported.value
3893
+ return ancestors.length === 0 || name !== 'default'
3894
+ })
3895
+ )
3896
+ if (statement.type !== 'ExportAllDeclaration') return false
3897
+ if (statement.exported !== null || !statement.source.value.startsWith('.')) return true
3898
+ const target = resolve(dirname(path), statement.source.value.replace(/\\.js$/u, '.ts'))
3899
+ return matchesDistributionSourceExports(target, [...ancestors, path])
3502
3900
  })
3901
+ }
3503
3902
 
3504
- // The absent subpath is the firing control: a resolution that reports nothing
3505
- // for every published entry has not been shown to resolve anything at all. Each
3506
- // module format carries its own control, because a format that resolves nothing
3507
- // is silent for the same reason a resolution that resolves nothing is.
3508
- it('compiles a consumer under every module resolution [requires the registry]', (context) => {
3509
- const stage = requireStage(context)
3510
- const name = readManifestName(join(stage.installed, 'package.json'))
3511
- const readings = RESOLUTIONS.flatMap((driver) =>
3512
- FORMATS.flatMap(([extension, format]) => {
3513
- const written = selectEntries(stage.entries, driver.conditions[format])
3514
- if (written.length === 0) return []
3515
- const label = \`\${driver.label}.\${extension}\`
3516
- const probe = \`probe.\${label}\`
3517
- const specifiers = written.map((entry) => entry.specifier)
3518
- writeConsumerProbe(stage, probe, specifiers)
3519
- const project = writeProject(stage, probe, driver, [\`./\${probe}\`])
3520
- const messages = checkProject(stage, project).map((message) => \`\${label}: \${message}\`)
3521
- const control = \`control.\${label}\`
3522
- writeConsumerProbe(stage, control, [\`\${name}\${ABSENT_SUBPATH}\`])
3523
- const refused = writeProject(stage, control, driver, [\`./\${control}\`])
3524
- return [{ messages, silent: checkProject(stage, refused).length === 0 ? [label] : [] }]
3525
- }),
3526
- )
3527
- expect(readings.flatMap(({ messages }) => messages)).toStrictEqual([])
3528
- expect(readings.flatMap(({ silent }) => silent)).toStrictEqual([])
3903
+ /**
3904
+ * Scans installed declaration faces for names declared by multiple environments.
3905
+ * @param installed - The installed package root.
3906
+ * @param source - The workspace holding the source entries being published.
3907
+ * @returns The repeated names with their declaring faces.
3908
+ * @throws Thrown when a source entry exports names but its roll-up declares none.
3909
+ */
3910
+ export function scanDistributionDeclarations(
3911
+ installed: string,
3912
+ source: string = DISTRIBUTION_ROOT,
3913
+ ): readonly string[] {
3914
+ const declarations = collectDistributionFiles(join(installed, 'dist'))
3915
+ .filter(matchesDistributionDeclaration)
3916
+ .map((path) => ({
3917
+ path,
3918
+ face: readDistributionFace(installed, path),
3919
+ names: readDistributionNames(path),
3920
+ }))
3921
+ const entries = collectDistributionFiles(join(source, 'src')).filter((path) => {
3922
+ const parts = relativePath(source, path).split(sep)
3923
+ return parts.length === 3 && parts[2] === 'index.ts'
3529
3924
  })
3530
- {{guard}}})
3531
-
3532
- describe('installed entries', () => {
3533
- it('publishes what it declares to a Node import, and no more', (context) => {
3534
- const stage = requireStage(context)
3535
- const entries = stage.entries.filter((entry) => entry.importable)
3536
- if (entries.length === 0) return context.skip('No installed entry supports Node import')
3537
- for (const entry of entries) {
3538
- if (!entry.declaration.importable) {
3539
- throw new Error(\`\${entry.subpath} publishes no import declaration\`)
3540
- }
3541
- const published = driveRuntime(stage, entry.specifier, ESM_DRIVER)
3542
- const drivers = selectDrivers(entry, 'module')
3543
- expect(drivers).not.toStrictEqual([])
3544
- const reported = drivers.flatMap((driver) =>
3545
- checkSurface(stage, { entry, extension: 'ts', published, driver }),
3925
+ const published = entries.filter((path) => matchesDistributionSourceExports(path))
3926
+ for (const path of published) {
3927
+ const face = relativePath(source, path).split(sep)[1]
3928
+ if (!declarations.some((entry) => entry.face === face && entry.names.length > 0))
3929
+ throw new Error(
3930
+ \`The \${face} source entry exports names but its declaration roll-up declares none\`,
3546
3931
  )
3547
- expect(reported).toStrictEqual([])
3548
- }
3932
+ }
3933
+ if (published.length === 0)
3934
+ console.info('No source entry exports names, so no declaration names are required')
3935
+ const names = [...new Set(declarations.flatMap((entry) => entry.names))].sort()
3936
+ return names.flatMap((name) => {
3937
+ const faces = [
3938
+ ...new Set(
3939
+ declarations
3940
+ .filter((entry) => entry.names.includes(name))
3941
+ .map((entry) => entry.face)
3942
+ .filter((face) => face !== undefined),
3943
+ ),
3944
+ ].sort()
3945
+ return faces.length > 1 ? [\`\${name}: \${faces.join(', ')}\`] : []
3549
3946
  })
3947
+ }
3550
3948
 
3551
- it('publishes what it declares to a Node require, and no more', (context) => {
3552
- const stage = requireStage(context)
3553
- const entries = stage.entries.filter((entry) => entry.requirable)
3554
- if (entries.length === 0) return context.skip('No installed entry supports Node require')
3555
- for (const entry of entries) {
3556
- if (!entry.declaration.requirable) {
3557
- throw new Error(\`\${entry.subpath} publishes no require declaration\`)
3558
- }
3559
- const published = driveRuntime(stage, entry.specifier, CJS_DRIVER)
3560
- const drivers = selectDrivers(entry, 'commonjs')
3561
- expect(drivers).not.toStrictEqual([])
3562
- const reported = drivers.flatMap((driver) =>
3563
- checkSurface(stage, { entry, extension: 'cts', published, driver }),
3564
- )
3565
- expect(reported).toStrictEqual([])
3566
- }
3567
- })
3568
- {{drive}}})
3949
+ /**
3950
+ * Checks the installed manifest against npm's normalization in an owned scratch copy.
3951
+ * @param installed - The installed package root, read only.
3952
+ * @param scratch - The suite's owned scratch root and npm cache parent.
3953
+ * @returns The top-level keys changed by npm. Refuses an unsuccessful npm command.
3954
+ */
3955
+ export function checkDistributionManifest(installed: string, scratch: string): readonly string[] {
3956
+ const directory = mkdtempSync(join(scratch, 'manifest-'))
3957
+ try {
3958
+ const path = join(directory, 'package.json')
3959
+ writeFileSync(path, readFileSync(join(installed, 'package.json')))
3960
+ const before = readDistributionJSON(path)
3961
+ const result = runDistributionNpm(['pkg', 'fix'], directory, scratch)
3962
+ if (result.status !== 0)
3963
+ throw new Error(\`npm pkg fix failed: \${readDistributionOutput(result)}\`)
3964
+ const after = readDistributionJSON(path)
3965
+ if (!isDistributionRecord(before) || !isDistributionRecord(after))
3966
+ throw new Error('Unreadable manifest')
3967
+ return [...new Set([...Object.keys(before), ...Object.keys(after)])]
3968
+ .filter((key) => !isDeepStrictEqual(before[key], after[key]))
3969
+ .sort()
3970
+ } finally {
3971
+ rmSync(directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 })
3972
+ }
3973
+ }
3974
+
3975
+ /**
3976
+ * Builds independently registered observations over the suite's installed consumer.
3977
+ * @param suite - The lifecycle shared by the proof's hooks and observations.
3978
+ * @returns The case rows whose observations each require one assertion.
3979
+ */
3980
+ export function buildDistributionCases(
3981
+ suite: DistributionSuiteInterface,
3982
+ ): readonly DistributionCase[] {
3983
+ return [
3984
+ {
3985
+ label: 'classifies synthetic export mappings without a registry stage',
3986
+ observe: () => {
3987
+ const root = join(suite.scratch, 'classifiers')
3988
+ writeDistributionFile(
3989
+ join(root, 'package.json'),
3990
+ JSON.stringify({
3991
+ type: 'commonjs',
3992
+ exports: {
3993
+ condition: { browser: './b.js', default: './n.js' },
3994
+ convention: { default: './dist/src/browser/index.js' },
3995
+ universal: { default: './shared.js' },
3996
+ 'import-shared': {
3997
+ browser: './shared.mjs',
3998
+ import: './shared.mjs',
3999
+ default: './node.js',
4000
+ },
4001
+ 'require-shared': {
4002
+ browser: './shared.cjs',
4003
+ require: './shared.cjs',
4004
+ default: './node.js',
4005
+ },
4006
+ node: { node: './node.js', default: './node.js' },
4007
+ silent: { 'module-sync': './x.cjs', import: './x.mjs' },
4008
+ module: { require: './x.mjs' },
4009
+ 'nested-module': { require: './module/x.js' },
4010
+ 'nested-commonjs': { require: './commonjs/x.js' },
4011
+ esm: { import: './x.mjs' },
4012
+ },
4013
+ }),
4014
+ )
4015
+ writeDistributionFile(join(root, 'module/package.json'), '{ "type": "module" }\\n')
4016
+ writeDistributionFile(join(root, 'commonjs/package.json'), '{ "type": "commonjs" }\\n')
4017
+ const manifest = readDistributionJSON(join(root, 'package.json'))
4018
+ if (!isDistributionRecord(manifest) || !isDistributionRecord(manifest.exports)) {
4019
+ throw new Error('The classifier fixture declares no exports map')
4020
+ }
4021
+ const mappings = manifest.exports
4022
+
4023
+ return {
4024
+ actual: {
4025
+ browser: {
4026
+ condition: resolvesDistributionBrowser(mappings.condition),
4027
+ convention: resolvesDistributionBrowser(mappings.convention),
4028
+ universal: resolvesDistributionBrowser(mappings.universal),
4029
+ import: resolvesDistributionBrowser(mappings['import-shared']),
4030
+ require: resolvesDistributionBrowser(mappings['require-shared']),
4031
+ node: resolvesDistributionBrowser(mappings.node),
4032
+ },
4033
+ commonjs: {
4034
+ silent: resolvesDistributionCommonJS(mappings.silent, root),
4035
+ module: resolvesDistributionCommonJS(mappings.module, root),
4036
+ nestedModule: resolvesDistributionCommonJS(mappings['nested-module'], root),
4037
+ nestedCommonJS: resolvesDistributionCommonJS(mappings['nested-commonjs'], root),
4038
+ esm: resolvesDistributionCommonJS(mappings.esm, root),
4039
+ },
4040
+ },
4041
+ expected: {
4042
+ browser: {
4043
+ condition: true,
4044
+ convention: true,
4045
+ universal: false,
4046
+ import: false,
4047
+ require: false,
4048
+ node: false,
4049
+ },
4050
+ commonjs: {
4051
+ silent: true,
4052
+ module: false,
4053
+ nestedModule: false,
4054
+ nestedCommonJS: true,
4055
+ esm: false,
4056
+ },
4057
+ },
4058
+ }
4059
+ },
4060
+ },
4061
+ {
4062
+ label: 'keeps installed JavaScript sources in their own environment [requires the registry]',
4063
+ observe: (context) => {
4064
+ const stage = suite.read(context)
4065
+ return { actual: scanDistributionSourceMaps(stage.installed), expected: [] }
4066
+ },
4067
+ },
4068
+ {
4069
+ label: 'declares each installed name in one environment [requires the registry]',
4070
+ observe: (context) => {
4071
+ const stage = suite.read(context)
4072
+ const faces = new Set(
4073
+ collectDistributionFiles(join(stage.installed, 'dist'))
4074
+ .filter(matchesDistributionDeclaration)
4075
+ .map((path) => readDistributionFace(stage.installed, path))
4076
+ .filter((face) => face !== undefined),
4077
+ )
4078
+ const findings = scanDistributionDeclarations(stage.installed)
4079
+ if (faces.size <= 1)
4080
+ console.info(
4081
+ 'Fewer than two declaration faces are installed, so no cross-face copy exists',
4082
+ )
4083
+ return { actual: findings, expected: [] }
4084
+ },
4085
+ },
4086
+ {
4087
+ label: 'keeps the installed manifest fixed under npm pkg fix [requires the registry]',
4088
+ observe: (context) => ({
4089
+ actual: checkDistributionManifest(suite.read(context).installed, suite.scratch),
4090
+ expected: [],
4091
+ }),
4092
+ },
4093
+ {
4094
+ label: 'packs one archive and installs it in isolation [requires the registry]',
4095
+ observe: (context) => {
4096
+ const stage = suite.read(context)
4097
+ return {
4098
+ actual: {
4099
+ archives: stage.archives.length,
4100
+ installed: existsSync(join(stage.installed, 'package.json')),
4101
+ entries: stage.entries.length > 0,
4102
+ },
4103
+ expected: { archives: 1, installed: true, entries: true },
4104
+ }
4105
+ },
4106
+ },
4107
+ {
4108
+ label: 'ships every relative target its exports map names [requires the registry]',
4109
+ observe: (context) => {
4110
+ const stage = suite.read(context)
4111
+ const relative = stage.targets.filter((target) => target.startsWith('./'))
4112
+ return {
4113
+ actual: {
4114
+ populated: relative.length > 0,
4115
+ missing: relative.filter((target) => !existsSync(join(stage.installed, target))),
4116
+ },
4117
+ expected: { populated: true, missing: [] },
4118
+ }
4119
+ },
4120
+ },
4121
+ {
4122
+ label: 'declares types for every module it publishes [requires the registry]',
4123
+ observe: (context) => {
4124
+ const stage = suite.read(context)
4125
+ const partitioned = [
4126
+ ...stage.entries.map((entry) => entry.subpath),
4127
+ ...stage.undeclared,
4128
+ ...stage.excluded,
4129
+ ]
4130
+ return {
4131
+ actual: {
4132
+ undeclared: stage.undeclared,
4133
+ partitioned: partitioned.sort(),
4134
+ unreachable: stage.entries
4135
+ .filter((entry) => !entry.importable && !entry.requirable && !entry.browsable)
4136
+ .map((entry) => entry.subpath),
4137
+ untypable: filterDistributionUntypable(stage.entries, stage.installed).map(
4138
+ (entry) => entry.subpath,
4139
+ ),
4140
+ },
4141
+ expected: {
4142
+ undeclared: [],
4143
+ partitioned: [...stage.subpaths].sort(),
4144
+ unreachable: [],
4145
+ untypable: [],
4146
+ },
4147
+ }
4148
+ },
4149
+ },
4150
+ {
4151
+ label: 'refuses a subpath its exports map does not name [requires the registry]',
4152
+ observe: (context) => {
4153
+ const stage = suite.read(context)
4154
+ const name = readDistributionManifestName(join(stage.installed, 'package.json'))
4155
+ const result = runDistributionNode(
4156
+ [join(stage.consumer, DISTRIBUTION_ESM_DRIVER), \`\${name}\${DISTRIBUTION_ABSENT_SUBPATH}\`],
4157
+ stage.consumer,
4158
+ )
4159
+ return {
4160
+ actual: {
4161
+ refused: result.status !== 0,
4162
+ coded: readDistributionOutput(result).includes('ERR_PACKAGE_PATH_NOT_EXPORTED'),
4163
+ },
4164
+ expected: { refused: true, coded: true },
4165
+ }
4166
+ },
4167
+ },
4168
+ {
4169
+ label: 'compiles a consumer under every module resolution [requires the registry]',
4170
+ observe: (context) => {
4171
+ const stage = suite.read(context)
4172
+ const name = readDistributionManifestName(join(stage.installed, 'package.json'))
4173
+ const readings = DISTRIBUTION_RESOLUTIONS.flatMap((driver) =>
4174
+ DISTRIBUTION_FORMATS.flatMap(([extension, format]) => {
4175
+ const written = filterDistributionEntries(stage.entries, driver.conditions[format])
4176
+ if (written.length === 0) return []
4177
+ const label = \`\${driver.label}.\${extension}\`
4178
+ const probe = \`probe.\${label}\`
4179
+ const specifiers = written.map((entry) => entry.specifier)
4180
+ writeDistributionConsumerProbe(stage, probe, specifiers)
4181
+ const project = writeDistributionProject(stage, probe, driver, [\`./\${probe}\`])
4182
+ const messages = checkDistributionProject(stage, project).map(
4183
+ (message) => \`\${label}: \${message}\`,
4184
+ )
4185
+ const control = \`control.\${label}\`
4186
+ writeDistributionConsumerProbe(stage, control, [
4187
+ \`\${name}\${DISTRIBUTION_ABSENT_SUBPATH}\`,
4188
+ ])
4189
+ const refused = writeDistributionProject(stage, control, driver, [\`./\${control}\`])
4190
+ return [
4191
+ {
4192
+ messages,
4193
+ silent: checkDistributionProject(stage, refused).length === 0 ? [label] : [],
4194
+ },
4195
+ ]
4196
+ }),
4197
+ )
4198
+ return {
4199
+ actual: {
4200
+ messages: readings.flatMap(({ messages }) => messages),
4201
+ silent: readings.flatMap(({ silent }) => silent),
4202
+ },
4203
+ expected: { messages: [], silent: [] },
4204
+ }
4205
+ },
4206
+ },
4207
+ {
4208
+ label: 'publishes what it declares to a Node import, and no more',
4209
+ observe: (context) => {
4210
+ const stage = suite.read(context)
4211
+ const entries = stage.entries.filter((entry) => entry.importable)
4212
+ if (entries.length === 0) return context.skip('No installed entry supports Node import')
4213
+ const readings = entries.map((entry) => {
4214
+ if (!entry.declaration.importable)
4215
+ throw new Error(\`\${entry.subpath} publishes no import declaration\`)
4216
+ const published = driveDistributionRuntime(
4217
+ stage,
4218
+ entry.specifier,
4219
+ DISTRIBUTION_ESM_DRIVER,
4220
+ )
4221
+ const drivers = filterDistributionResolutions(entry, 'module')
4222
+ return {
4223
+ entry: entry.subpath,
4224
+ driven: drivers.length > 0,
4225
+ reported: drivers.flatMap((driver) =>
4226
+ checkDistributionSurface(stage, { entry, extension: 'ts', published, driver }),
4227
+ ),
4228
+ }
4229
+ })
4230
+ return {
4231
+ actual: readings,
4232
+ expected: entries.map((entry) => ({ entry: entry.subpath, driven: true, reported: [] })),
4233
+ }
4234
+ },
4235
+ },
4236
+ {
4237
+ label: 'publishes what it declares to a Node require, and no more',
4238
+ observe: (context) => {
4239
+ const stage = suite.read(context)
4240
+ const entries = stage.entries.filter((entry) => entry.requirable)
4241
+ if (entries.length === 0) return context.skip('No installed entry supports Node require')
4242
+ const readings = entries.map((entry) => {
4243
+ if (!entry.declaration.requirable)
4244
+ throw new Error(\`\${entry.subpath} publishes no require declaration\`)
4245
+ const published = driveDistributionRuntime(
4246
+ stage,
4247
+ entry.specifier,
4248
+ DISTRIBUTION_CJS_DRIVER,
4249
+ )
4250
+ const drivers = filterDistributionResolutions(entry, 'commonjs')
4251
+ return {
4252
+ entry: entry.subpath,
4253
+ driven: drivers.length > 0,
4254
+ reported: drivers.flatMap((driver) =>
4255
+ checkDistributionSurface(stage, { entry, extension: 'cts', published, driver }),
4256
+ ),
4257
+ }
4258
+ })
4259
+ return {
4260
+ actual: readings,
4261
+ expected: entries.map((entry) => ({ entry: entry.subpath, driven: true, reported: [] })),
4262
+ }
4263
+ },
4264
+ },
4265
+ ]
4266
+ }
4267
+ `,
4268
+ proof: `import {
4269
+ // These exports remain available to proofs emitted by earlier scaffold releases.
4270
+ buildDistributionCases,
4271
+ {{import}}createDistributionSuite,
4272
+ } from './setupDistribution.js'
4273
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
4274
+
4275
+ const DISTRIBUTION_SUITE = createDistributionSuite()
4276
+
4277
+ beforeAll(() => DISTRIBUTION_SUITE.start())
4278
+
4279
+ afterAll(() => DISTRIBUTION_SUITE.destroy())
4280
+
4281
+ describe('installed package consumer', () => {
4282
+ for (const row of buildDistributionCases(DISTRIBUTION_SUITE)) {
4283
+ it(\`\${row.label}\`, async (context) => {
4284
+ const observation = await row.observe(context)
4285
+ expect(observation.actual).toStrictEqual(observation.expected)
4286
+ })
4287
+ }
4288
+ {{guard}}{{drive}}})
3569
4289
  `,
3570
4290
  transport: `import { createServer } from 'node:http'
3571
4291
  `,
@@ -3576,8 +4296,9 @@ import type { Browser } from 'playwright'
3576
4296
  import { build } from 'vite'
3577
4297
  import { resolveBrowser, resolvePinnedBrowser } from '../configs/browsers.js'
3578
4298
  `,
3579
- helpers: `
3580
- const BROWSER_PAGE = \`<!doctype html>
4299
+ import: "checkDistributionBrowser,\n ",
4300
+ helpers: `/** Defines the page that loads a bundled browser consumer. */
4301
+ export const DISTRIBUTION_BROWSER_PAGE = \`<!doctype html>
3581
4302
  <html lang="en">
3582
4303
  <head>
3583
4304
  <meta charset="UTF-8" />
@@ -3589,7 +4310,13 @@ const BROWSER_PAGE = \`<!doctype html>
3589
4310
  </html>
3590
4311
  \`
3591
4312
 
3592
- function readContentType(path: string): string {
4313
+ /**
4314
+ * Infers the response media type from a bundle filename.
4315
+ *
4316
+ * @param path - The requested bundle path.
4317
+ * @returns The media type, defaulting to application/octet-stream.
4318
+ */
4319
+ export function inferDistributionContentType(path: string): string {
3593
4320
  if (path.endsWith('.html')) return 'text/html'
3594
4321
  if (path.endsWith('.js')) return 'text/javascript'
3595
4322
  if (path.endsWith('.css')) return 'text/css'
@@ -3600,7 +4327,13 @@ function readContentType(path: string): string {
3600
4327
  // \`resolveBrowser\` answers with provider options and never reports absence: its
3601
4328
  // last resort is a channel nothing verified. So the launch is attempted and its
3602
4329
  // rejection classified, rather than probed for and ruled on.
3603
- function describeBrowser(options: PlaywrightProviderOptions): string {
4330
+ /**
4331
+ * Renders the browser endpoint selected by provider options.
4332
+ *
4333
+ * @param options - The browser provider options.
4334
+ * @returns The endpoint, executable, channel, or bundled-browser description.
4335
+ */
4336
+ export function renderDistributionBrowser(options: PlaywrightProviderOptions): string {
3604
4337
  const endpoint = options.connectOptions?.wsEndpoint
3605
4338
  if (endpoint !== undefined) return \`the browser server at \${endpoint}\`
3606
4339
  const executable = options.launchOptions?.executablePath
@@ -3610,7 +4343,15 @@ function describeBrowser(options: PlaywrightProviderOptions): string {
3610
4343
  return 'the Chromium Playwright installed for itself'
3611
4344
  }
3612
4345
 
3613
- async function launchBrowser(options: PlaywrightProviderOptions): Promise<Browser> {
4346
+ /**
4347
+ * Launches or connects to the selected Chromium browser.
4348
+ *
4349
+ * @param options - The browser provider options.
4350
+ * @returns The real browser. Rejects when the selected browser is unavailable.
4351
+ */
4352
+ export async function launchDistributionBrowser(
4353
+ options: PlaywrightProviderOptions,
4354
+ ): Promise<Browser> {
3614
4355
  const endpoint = options.connectOptions?.wsEndpoint
3615
4356
  if (endpoint !== undefined) return chromium.connect(endpoint)
3616
4357
  return chromium.launch({ ...options.launchOptions, headless: true })
@@ -3619,11 +4360,21 @@ async function launchBrowser(options: PlaywrightProviderOptions): Promise<Browse
3619
4360
  // A consumer of one installed browser entry, bundled by the Vite toolchain this
3620
4361
  // workspace already declares. Nothing is stubbed: the bundle resolves the installed
3621
4362
  // package and its whole transitive graph as an application consuming it would.
3622
- async function bundleEntry(stage: Stage, entry: Entry): Promise<string> {
4363
+ /**
4364
+ * Bundles a browser consumer against an installed public entry.
4365
+ *
4366
+ * @param stage - The installed consumer.
4367
+ * @param entry - The browser publication.
4368
+ * @returns The bundle directory. Rejects when Vite cannot build the consumer.
4369
+ */
4370
+ export async function bundleDistributionEntry(
4371
+ stage: DistributionStage,
4372
+ entry: DistributionEntry,
4373
+ ): Promise<string> {
3623
4374
  const page = join(stage.consumer, 'pages', entry.subpath.replaceAll(/[^\\w]+/gu, '-'))
3624
4375
  const specifier = JSON.stringify(entry.specifier)
3625
- writeFile(join(page, 'index.html'), BROWSER_PAGE)
3626
- writeFile(
4376
+ writeDistributionFile(join(page, 'index.html'), DISTRIBUTION_BROWSER_PAGE)
4377
+ writeDistributionFile(
3627
4378
  join(page, 'main.js'),
3628
4379
  \`import * as entry from \${specifier}\\nglobalThis.subject = Object.keys(entry).sort()\\n\`,
3629
4380
  )
@@ -3640,7 +4391,17 @@ async function bundleEntry(stage: Stage, entry: Entry): Promise<string> {
3640
4391
  // The key set the bundled module publishes in a real browser, read off the page
3641
4392
  // once it has loaded over a loopback server. A module that never evaluated
3642
4393
  // publishes nothing, and a page error is raised rather than compared away.
3643
- async function readBrowserExports(browser: Browser, bundle: string): Promise<readonly string[]> {
4394
+ /**
4395
+ * Reads the names a real browser publishes from a served bundle.
4396
+ *
4397
+ * @param browser - The browser to drive.
4398
+ * @param bundle - The built consumer directory.
4399
+ * @returns The exported names. Rejects on a page error or an invalid name list.
4400
+ */
4401
+ export async function readDistributionBrowserExports(
4402
+ browser: Browser,
4403
+ bundle: string,
4404
+ ): Promise<readonly string[]> {
3644
4405
  const server = createServer((request, response) => {
3645
4406
  const asked = request.url === undefined || request.url === '/' ? '/index.html' : request.url
3646
4407
  const path = join(bundle, decodeURIComponent(asked))
@@ -3649,7 +4410,7 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
3649
4410
  response.end()
3650
4411
  return
3651
4412
  }
3652
- response.writeHead(200, { 'content-type': readContentType(path) })
4413
+ response.writeHead(200, { 'content-type': inferDistributionContentType(path) })
3653
4414
  response.end(readFileSync(path))
3654
4415
  })
3655
4416
  try {
@@ -3666,63 +4427,65 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
3666
4427
  await page.goto(\`http://127.0.0.1:\${String(address.port)}/\`, { waitUntil: 'load' })
3667
4428
  const published: unknown = await page.evaluate('globalThis.subject')
3668
4429
  if (failures.length > 0) throw new Error(\`The bundle raised \${failures.join(' | ')}\`)
3669
- if (!isNames(published)) throw new Error('The bundled module published no name list')
4430
+ if (!isDistributionNames(published))
4431
+ throw new Error('The bundled module published no name list')
3670
4432
  return published
3671
4433
  } finally {
3672
4434
  server.close()
3673
4435
  }
3674
4436
  }
3675
- `,
3676
- drive: `
3677
- it('publishes what it declares to a real browser [requires a browser]', async (context) => {
3678
- const stage = requireStage(context)
3679
- const entries = stage.entries.filter((entry) => entry.browsable)
3680
- if (entries.length === 0) return context.skip('No installed entry supports a browser')
3681
- for (const entry of entries) {
3682
- if (!entry.declaration.browsable) {
3683
- throw new Error(\`\${entry.subpath} publishes no browser declaration\`)
3684
- }
3685
- const options = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
3686
- const browser = await launchBrowser(options).catch((error: unknown) => {
3687
- const cause = \`\${describeBrowser(options)} was rejected: \${String(error)}\`
3688
- if (RELEASE) throw new Error(\`The release gate requires a browser, and \${cause}\`)
3689
- return context.skip(\`No browser launched. \${cause}\`)
4437
+ /**
4438
+ * Checks each browser publication against its installed declarations in a real browser.
4439
+ * @param stage - The installed consumer to drive.
4440
+ * @param context - The proof context that reports unavailable browser prerequisites.
4441
+ * @returns The compiler diagnostics each browser publication reports.
4442
+ */
4443
+ export async function checkDistributionBrowser(
4444
+ stage: DistributionStage,
4445
+ context: TestContext,
4446
+ ): Promise<readonly string[]> {
4447
+ const driver = DISTRIBUTION_BROWSER_RESOLUTION
4448
+ if (driver === undefined) throw new Error("DISTRIBUTION_RESOLUTIONS carries no 'bundler' row")
4449
+ const entries = stage.entries.filter((entry) => entry.browsable)
4450
+ if (entries.length === 0) return context.skip('No installed entry supports a browser')
4451
+ const readings: Array<readonly string[]> = []
4452
+ for (const entry of entries) {
4453
+ if (!entry.declaration.browsable) {
4454
+ throw new Error(\`\${entry.subpath} publishes no browser declaration\`)
4455
+ }
4456
+ const options = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
4457
+ const browser = await launchDistributionBrowser(options).catch((error: unknown) => {
4458
+ const cause = \`\${renderDistributionBrowser(options)} was rejected: \${String(error)}\`
4459
+ if (DISTRIBUTION_RELEASE) throw new Error(\`The release gate requires a browser, and \${cause}\`)
4460
+ return context.skip(\`No browser launched. \${cause}\`)
4461
+ })
4462
+ try {
4463
+ const bundle = await bundleDistributionEntry(stage, entry)
4464
+ const published = await readDistributionBrowserExports(browser, bundle)
4465
+ // A browser consumer reads the installed declarations through a bundler, so
4466
+ // that is the one driver this face answers under.
4467
+ const reported = checkDistributionSurface(stage, {
4468
+ entry,
4469
+ extension: 'ts',
4470
+ published,
4471
+ driver,
3690
4472
  })
3691
- try {
3692
- const bundle = await bundleEntry(stage, entry)
3693
- const published = await readBrowserExports(browser, bundle)
3694
- // A browser consumer reads the installed declarations through a bundler, so
3695
- // that is the one driver this face answers under.
3696
- const reported = checkSurface(stage, {
3697
- entry,
3698
- extension: 'ts',
3699
- published,
3700
- driver: BROWSER_DRIVER,
3701
- })
3702
- expect(reported).toStrictEqual([])
3703
- } finally {
3704
- await browser.close()
3705
- }
4473
+ readings.push(reported)
4474
+ } finally {
4475
+ await browser.close()
3706
4476
  }
4477
+ }
4478
+ return readings.flat()
4479
+ }
4480
+ `,
4481
+ drive: ` it('publishes what it declares to a real browser [requires a browser]', async (context) => {
4482
+ expect(await checkDistributionBrowser(DISTRIBUTION_SUITE.read(context), context)).toStrictEqual(
4483
+ [],
4484
+ )
3707
4485
  })
3708
4486
  `,
3709
- guard: `
3710
- // This proof drives a Node import and a Node require and carries no browser
3711
- // branch: the workspace published no browser face when it was written, and the
3712
- // browser drive measures the packed artifact, so only a published face is owed
3713
- // one. A private browser application does not select this branch. It declares the
3714
- // browser launcher and its Vitest browser provider and gets the generated browser
3715
- // configuration module beside it, but installed browser tooling does not stand for
3716
- // a published browser face. \`vite\` selects nothing either, though the branch
3717
- // imports it: scaffold puts \`vite\` in every workspace's base development
3718
- // dependencies, whatever that workspace publishes. The later Node
3719
- // entry filters retire each matching Node drive for a face published
3720
- // later, which leaves nothing measuring it. So it reddens here and names the
3721
- // subpath a browser branch is owed for. A workspace that gains one deletes this
3722
- // file and runs the \`repair\` verb, which writes the variant carrying that branch.
3723
- it('publishes no browser face this proof cannot drive [requires the registry]', (context) => {
3724
- const stage = requireStage(context)
3725
- const faces = stage.entries.filter((entry) => entry.browsable)
4487
+ guard: ` it('publishes no browser face this proof cannot drive [requires the registry]', (context) => {
4488
+ const faces = DISTRIBUTION_SUITE.read(context).entries.filter((entry) => entry.browsable)
3726
4489
  expect(faces.map((entry) => entry.subpath)).toStrictEqual([])
3727
4490
  })
3728
4491
  `
@@ -3972,7 +4735,7 @@ var isContent = stringOf({ max: MAX_ARTIFACT_BYTES });
3972
4735
  *
3973
4736
  * @remarks
3974
4737
  * Compose this ahead of an element guard so the item count is settled before
3975
- * anything walks the items. A hostile `length` accessor answers `false` here
4738
+ * anything walks the items. A throwing `length` accessor answers `false` here
3976
4739
  * rather than escaping as a thrown error.
3977
4740
  *
3978
4741
  * @example
@@ -4223,6 +4986,7 @@ var isBlueprint = recordOf({
4223
4986
  guides: isBoolean,
4224
4987
  integration: isBoolean,
4225
4988
  conformance: isBoolean,
4989
+ tree: isBoolean,
4226
4990
  service: isBoolean,
4227
4991
  vendors: andOf(isCollection, arrayOf(isString)),
4228
4992
  studies: isBoolean,
@@ -4404,7 +5168,7 @@ var isCatalogEntry = unionOf(recordOf({
4404
5168
  * whose every value is exact lowercase hexadecimal bytes; false otherwise.
4405
5169
  *
4406
5170
  * @remarks
4407
- * Read through the shared total key lens, so a hostile `ownKeys` trap and a
5171
+ * Read through the shared total key lens, so an `ownKeys` trap that throws and a
4408
5172
  * throwing accessor both answer `false` rather than escaping. There is no
4409
5173
  * dictionary combinator upstream to compose this from: the key law and the
4410
5174
  * value law are both this package's own.
@@ -6125,10 +6889,11 @@ function blueprintToScripts(blueprint) {
6125
6889
  "npm run test:policy",
6126
6890
  "npm run test:config",
6127
6891
  ...blueprint.setup.includes("node") || blueprint.global ? ["npm run test:setup"] : [],
6128
- ...blueprint.setup.includes("browser") || targets.length > 0 ? ["npm run test:setup:browser"] : [],
6892
+ ...blueprint.setup.includes("browser") ? ["npm run test:setup:browser"] : [],
6129
6893
  ...blueprint.skills ? ["npm run test:skills"] : [],
6130
6894
  ...blueprint.guides ? ["npm run test:guides"] : [],
6131
6895
  ...blueprint.conformance ? ["npm run test:conformance"] : [],
6896
+ ...blueprint.tree ? ["npm run test:tree"] : [],
6132
6897
  ...integrates ? ["npm run test:integration"] : [],
6133
6898
  ...!publishes && blueprint.studies ? ["npm run test:studies"] : [],
6134
6899
  ...!publishes && blueprint.service ? ["npm run test:service"] : []
@@ -6157,7 +6922,7 @@ function blueprintToScripts(blueprint) {
6157
6922
  "test:policy": `${vitest} --project policy`,
6158
6923
  "test:config": `${vitest} --project config`,
6159
6924
  ...blueprint.setup.includes("node") || blueprint.global ? { "test:setup": `${vitest} --project setup` } : {},
6160
- ...blueprint.setup.includes("browser") || targets.length > 0 ? { "test:setup:browser": `${vitest} --project setup:browser` } : {},
6925
+ ...blueprint.setup.includes("browser") ? { "test:setup:browser": `${vitest} --project setup:browser` } : {},
6161
6926
  ...blueprint.journey && blueprint.app.includes("browser") ? {
6162
6927
  "test:journey": journey,
6163
6928
  ...Object.fromEntries(application.map((name) => [`test:journey:${name}`, `${journey} --mode ${name}`]))
@@ -6165,6 +6930,7 @@ function blueprintToScripts(blueprint) {
6165
6930
  ...blueprint.skills ? { "test:skills": `${vitest} --project skills` } : {},
6166
6931
  ...blueprint.guides ? { "test:guides": `node --experimental-strip-types ${GUIDES_TEST_PATH}` } : {},
6167
6932
  ...blueprint.conformance ? { "test:conformance": `${vitest} --project conformance` } : {},
6933
+ ...blueprint.tree ? { "test:tree": `${vitest} --project tree` } : {},
6168
6934
  "test:probe": "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe",
6169
6935
  "test:bench": "vitest bench --config vite.config.ts --no-cache --project probe",
6170
6936
  ...publishes ? { "test:distribution": `${vitest} --project distribution` } : {},
@@ -6350,7 +7116,7 @@ function blueprintToManifest(blueprint) {
6350
7116
  type: "git",
6351
7117
  url: `git+https://github.com/orkestrel/${blueprint.name}.git`
6352
7118
  } } : {},
6353
- ...blueprint.bin ? { bin: { [blueprint.name]: "./dist/bin/main.js" } } : {},
7119
+ ...blueprint.bin ? { bin: { [blueprint.name]: BIN_OUTPUT_PATH } } : {},
6354
7120
  files: [
6355
7121
  blueprint.bin || publishes ? "dist/src" : "dist/app",
6356
7122
  ...blueprint.bin ? ["dist/bin"] : [],
@@ -6361,7 +7127,7 @@ function blueprintToManifest(blueprint) {
6361
7127
  ],
6362
7128
  type: "module",
6363
7129
  ...publishes ? {
6364
- sideEffects: targets.length > 0 || blueprint.bin ? [...targets.length > 0 ? ["**/*.css", "**/*.scss"] : [], ...blueprint.bin ? [`./${BIN_ENTRY_PATH}`, "./dist/bin/main.js"] : []] : false,
7130
+ sideEffects: targets.length > 0 || blueprint.bin ? [...targets.length > 0 ? ["**/*.css", "**/*.scss"] : [], ...blueprint.bin ? [`./${BIN_ENTRY_PATH}`, `./${BIN_OUTPUT_PATH}`] : []] : false,
6365
7131
  ...blueprint.src.length > 0 ? entry : {},
6366
7132
  exports: blueprintToExports(blueprint),
6367
7133
  publishConfig: { access: "public" }
@@ -6512,10 +7278,11 @@ function blueprintToProjects(blueprint) {
6512
7278
  "policy",
6513
7279
  "config",
6514
7280
  ...blueprint.setup.includes("node") || blueprint.global ? ["setup"] : [],
6515
- ...blueprint.setup.includes("browser") || styled ? ["setup:browser"] : [],
7281
+ ...blueprint.setup.includes("browser") ? ["setup:browser"] : [],
6516
7282
  ...blueprint.guides ? ["guides"] : [],
6517
7283
  ...blueprint.skills ? ["skills"] : [],
6518
7284
  ...blueprint.conformance ? ["conformance"] : [],
7285
+ ...blueprint.tree ? ["tree"] : [],
6519
7286
  ...blueprint.service ? ["service"] : [],
6520
7287
  ...blueprint.studies ? ["studies"] : [],
6521
7288
  ...blueprint.src.length > 0 || styled ? ["distribution"] : [],
@@ -6581,13 +7348,10 @@ function blueprintToRootVite(blueprint) {
6581
7348
  const styled = sheets.length > 0 || blueprint.themes;
6582
7349
  const global = blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "";
6583
7350
  const imports = [
7351
+ ...styled || machinery.showcase ? [`import { ${[...styled ? ["existsSync"] : [], ...machinery.showcase ? ["renameSync"] : []].join(", ")} } from 'node:fs'`] : [],
6584
7352
  ...machinery.browser ? ["import { playwright } from '@vitest/browser-playwright'"] : [],
6585
7353
  ...machinery.frameworks.includes("vue") ? ["import vue from '@vitejs/plugin-vue'"] : [],
6586
- ...machinery.showcase ? [
6587
- "import { viteSingleFile } from 'vite-plugin-singlefile'",
6588
- "import { renameSync } from 'node:fs'",
6589
- "import { resolve as resolvePath } from 'node:path'"
6590
- ] : []
7354
+ ...machinery.showcase ? ["import { viteSingleFile } from 'vite-plugin-singlefile'", "import { resolve as resolvePath } from 'node:path'"] : []
6591
7355
  ];
6592
7356
  const external = "external: resolveSourceExternal,";
6593
7357
  const factories = [
@@ -6650,6 +7414,7 @@ function blueprintToRootVite(blueprint) {
6650
7414
  ...planned.has("guides") ? [CONFIG_TEMPLATES.factories.guides] : [],
6651
7415
  ...planned.has("skills") ? [CONFIG_TEMPLATES.factories.skills] : [],
6652
7416
  ...planned.has("conformance") ? [CONFIG_TEMPLATES.factories.conformance] : [],
7417
+ ...planned.has("tree") ? [CONFIG_TEMPLATES.factories.tree] : [],
6653
7418
  ...planned.has("service") ? [CONFIG_TEMPLATES.factories.service] : [],
6654
7419
  ...planned.has("studies") ? [CONFIG_TEMPLATES.factories.studies] : [],
6655
7420
  ...planned.has("distribution") ? [CONFIG_TEMPLATES.factories.distribution] : [],
@@ -6674,6 +7439,7 @@ function blueprintToRootVite(blueprint) {
6674
7439
  ...planned.has("guides") ? ["guides"] : [],
6675
7440
  ...planned.has("skills") ? ["skills"] : [],
6676
7441
  ...planned.has("conformance") ? ["conformance"] : [],
7442
+ ...planned.has("tree") ? ["tree"] : [],
6677
7443
  ...planned.has("service") ? ["service"] : [],
6678
7444
  ...planned.has("studies") ? ["studies"] : [],
6679
7445
  ...planned.has("distribution") ? ["distribution"] : [],
@@ -7039,12 +7805,12 @@ function blueprintToSourceArtifacts(blueprint) {
7039
7805
  * path. `studies` emits nothing: the root configuration names no study harness,
7040
7806
  * so `tests/setupStudy.ts` is the package's own module.
7041
7807
  *
7042
- * The distribution proof is emitted, and the same test separates it from those
7043
- * two: its subject is the packed tarball rather than anything only the package
7044
- * knows, so every assertion derives from the installed tree at run time and
7045
- * nothing has to be named. It follows the published source it packs, and it is
7046
- * the one artifact here claimed by presence: a target lacking it reports as
7047
- * drift, and a package that replaced it with a better proof keeps that proof.
7808
+ * The content-owned distribution harness `tests/setupDistribution.ts` and the presence-owned
7809
+ * proof `tests/distribution.test.ts` are emitted because the proof measures the packed tarball
7810
+ * rather than anything only the package knows. Every assertion derives from the installed tree
7811
+ * at run time, so nothing has to be named. The proof follows the published source it packs and
7812
+ * alone is claimed by presence: a target lacking the proof reports as drift, and a package that
7813
+ * replaced the proof with a better one keeps that proof.
7048
7814
  *
7049
7815
  * @example
7050
7816
  * ```ts
@@ -7090,8 +7856,7 @@ function blueprintToTestArtifacts(blueprint) {
7090
7856
  origin: "template",
7091
7857
  content: fillTemplate(ARTIFACT_TEMPLATES.tests.sheet, {
7092
7858
  name,
7093
- root: `${tests}../`,
7094
- setup: `${tests}setupStyles.js`
7859
+ root: `${tests}../`
7095
7860
  })
7096
7861
  };
7097
7862
  });
@@ -7103,7 +7868,7 @@ function blueprintToTestArtifacts(blueprint) {
7103
7868
  environment: "browser",
7104
7869
  content: blueprint.journey ? ARTIFACT_TEMPLATES.tests.browser : ARTIFACT_TEMPLATES.tests.setup
7105
7870
  }] : [];
7106
- const servers = blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin || blueprint.conformance ? [{
7871
+ const servers = blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin || blueprint.conformance || blueprint.tree ? [{
7107
7872
  path: "tests/setupServer.ts",
7108
7873
  group: "tests",
7109
7874
  ownership: "birth",
@@ -7178,20 +7943,28 @@ function blueprintToTestArtifacts(blueprint) {
7178
7943
  })
7179
7944
  })).toArray();
7180
7945
  const distribution = ARTIFACT_TEMPLATES.tests.distribution;
7181
- const distributions = blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes ? [blueprint.src.includes("browser")].map((browser) => ({
7182
- path: DISTRIBUTION_TEST_PATH,
7946
+ const distributions = blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes ? [blueprint.src.includes("browser")].flatMap((browser) => [{
7947
+ path: DISTRIBUTION_SETUP_PATH,
7183
7948
  group: "tests",
7184
- ownership: "presence",
7949
+ ownership: "content",
7185
7950
  origin: "template",
7186
- content: fillTemplate(distribution.proof, {
7951
+ content: fillTemplate(distribution.module, {
7187
7952
  types: browser ? distribution.types : "",
7188
7953
  transport: browser ? distribution.transport : "",
7189
7954
  launcher: browser ? distribution.launcher : "",
7190
- helpers: browser ? distribution.helpers : "",
7955
+ helpers: browser ? distribution.helpers : ""
7956
+ })
7957
+ }, {
7958
+ path: DISTRIBUTION_TEST_PATH,
7959
+ group: "tests",
7960
+ ownership: "presence",
7961
+ origin: "template",
7962
+ content: fillTemplate(distribution.proof, {
7963
+ import: browser ? distribution.import : "",
7191
7964
  drive: browser ? distribution.drive : "",
7192
7965
  guard: browser ? "" : distribution.guard
7193
7966
  })
7194
- })) : [];
7967
+ }]) : [];
7195
7968
  const integrations = blueprint.integration ? [[...Iterator.from(blueprint.src).map((environment) => ({
7196
7969
  entry: environment === "core" ? "srcCore" : environment === "browser" ? "srcBrowser" : "srcServer",
7197
7970
  specifier: `@src/${environment}`
@@ -7226,7 +7999,7 @@ function blueprintToTestArtifacts(blueprint) {
7226
7999
  content: ARTIFACT_TEMPLATES.tests.setup
7227
8000
  },
7228
8001
  ...faces,
7229
- ...styles,
8002
+ ...styles.filter(({ content }) => content.length > 0),
7230
8003
  ...sheetTests,
7231
8004
  ...browsers,
7232
8005
  ...servers,
@@ -8384,13 +9157,14 @@ function createBlueprint(name, input) {
8384
9157
  peers: input?.peers ?? [],
8385
9158
  extras: input?.extras ?? [],
8386
9159
  version: input?.version ?? "0.0.1",
8387
- engines: input?.engines ?? DEFAULT_ENGINES,
9160
+ engines: input?.engines ?? ">=22.18.0",
8388
9161
  overrides: input?.overrides ?? [],
8389
9162
  bin: input?.bin ?? false,
8390
9163
  setup: input?.setup ?? [],
8391
9164
  guides: input?.guides ?? false,
8392
9165
  integration: input?.integration ?? false,
8393
9166
  conformance: input?.conformance ?? false,
9167
+ tree: input?.tree ?? false,
8394
9168
  service: input?.service ?? false,
8395
9169
  vendors: input?.vendors ?? [],
8396
9170
  studies: input?.studies ?? false,
@@ -8704,6 +9478,6 @@ var Compiler = class {
8704
9478
  }
8705
9479
  };
8706
9480
  //#endregion
8707
- export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, AXES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, BROWSE_DEV_DEPENDENCIES, BROWSE_UPSTREAM, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, FRAMEWORKS, FRAMEWORK_MATRIX, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, RESERVED_SHEET_NAMES, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SHOWCASE_OVERRIDES, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, STUDIES_TEST_INCLUDE, STYLES_DEV_DEPENDENCIES, STYLES_ENTRY_PATH, SURFACES, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToExports, blueprintToFaces, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToManifestOverrides, blueprintToOptimizedDependencies, blueprintToOrchestrationArtifacts, blueprintToProjects, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSheets, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, computeTotal, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, insertManifestDependencies, insertManifestSection, isArtifact, isAudit, isBlueprint, isBrowserExtension, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isExtension, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSheetName, isSnapshot, isStylesExtension, isSurface, iterateAnswers, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseExtension, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestOverride, replaceManifestOverrides, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, scanManifestTokens, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
9481
+ export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, AXES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, BIN_OUTPUT_PATH, BROWSE_DEV_DEPENDENCIES, BROWSE_UPSTREAM, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_SETUP_PATH, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, FRAMEWORKS, FRAMEWORK_MATRIX, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, RESERVED_SHEET_NAMES, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SHOWCASE_OVERRIDES, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, STUDIES_TEST_INCLUDE, STYLES_DEV_DEPENDENCIES, STYLES_ENTRY_PATH, SURFACES, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, TREE_TEST_PATH, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToExports, blueprintToFaces, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToManifestOverrides, blueprintToOptimizedDependencies, blueprintToOrchestrationArtifacts, blueprintToProjects, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSheets, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, computeTotal, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, insertManifestDependencies, insertManifestSection, isArtifact, isAudit, isBlueprint, isBrowserExtension, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isExtension, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSheetName, isSnapshot, isStylesExtension, isSurface, iterateAnswers, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseExtension, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestOverride, replaceManifestOverrides, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, scanManifestTokens, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
8708
9482
 
8709
9483
  //# sourceMappingURL=index.js.map