@orkestrel/scaffold 0.0.63 → 0.0.65

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 (57) hide show
  1. package/README.md +29 -104
  2. package/dist/bin/main.js +95 -27
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/CLAUDE.md +6 -0
  6. package/dist/host/agents/orchestration.md +23 -15
  7. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
  12. package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
  13. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
  14. package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
  15. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
  16. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
  17. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  18. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
  19. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
  20. package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
  21. package/dist/host/claude/agents/orkestrel.md +56 -56
  22. package/dist/host/claude/agents/reviewer.md +13 -0
  23. package/dist/host/claude/rules/architecture.md +51 -45
  24. package/dist/host/claude/rules/documentation.md +18 -1
  25. package/dist/host/claude/rules/portability.md +2 -0
  26. package/dist/host/claude/rules/quality.md +1 -1
  27. package/dist/host/claude/rules/tests.md +12 -11
  28. package/dist/host/claude/rules/typescript.md +5 -0
  29. package/dist/host/claude/rules/workspace.md +25 -20
  30. package/dist/host/claude/rules/writing.md +4 -0
  31. package/dist/host/claude/settings.json +1 -1
  32. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
  33. package/dist/host/codex/agents/orkestrel.toml +3 -3
  34. package/dist/host/codex/agents/reviewer.toml +4 -2
  35. package/dist/host/configs/helpers.ts +311 -2
  36. package/dist/host/configs/policy.ts +1100 -51
  37. package/dist/host/dotfiles/oxlintrc.json +72 -1
  38. package/dist/host/guides/guide.md +749 -222
  39. package/dist/host/guides/scaffold.md +529 -394
  40. package/dist/host/manifest.json +53 -40
  41. package/dist/host/scripts/ollama.sh +322 -13
  42. package/dist/host/tests/config.test.ts +1200 -16
  43. package/dist/host/tests/policy.test.ts +157 -173
  44. package/dist/host/tests/setupPolicy.ts +522 -1007
  45. package/dist/src/core/index.cjs +402 -287
  46. package/dist/src/core/index.cjs.map +1 -1
  47. package/dist/src/core/index.d.cts +160 -128
  48. package/dist/src/core/index.d.ts +160 -128
  49. package/dist/src/core/index.js +400 -286
  50. package/dist/src/core/index.js.map +1 -1
  51. package/dist/src/server/index.cjs +28 -21
  52. package/dist/src/server/index.cjs.map +1 -1
  53. package/dist/src/server/index.d.cts +38 -33
  54. package/dist/src/server/index.d.ts +38 -33
  55. package/dist/src/server/index.js +28 -21
  56. package/dist/src/server/index.js.map +1 -1
  57. package/package.json +18 -19
@@ -4,8 +4,8 @@ let _orkestrel_template = require("@orkestrel/template");
4
4
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
5
  var package_default = {
6
6
  name: "@orkestrel/scaffold",
7
- version: "0.0.63",
8
- description: "Scaffold workspaces with five commands: new, audit, repair, catalog, and overwrite.",
7
+ version: "0.0.65",
8
+ description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
9
9
  keywords: [
10
10
  "audit",
11
11
  "catalog",
@@ -74,7 +74,7 @@ var package_default = {
74
74
  "test:src:bin": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:bin",
75
75
  "test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
76
76
  "test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
77
- "test:guides": "vitest run --config vite.config.ts --no-cache --reporter=dot --project guides",
77
+ "test:guides": "node --experimental-strip-types tests/guides.test.ts",
78
78
  "test:distribution": "vitest run --config vite.config.ts --no-cache --reporter=dot --project distribution",
79
79
  "test:probe": "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe",
80
80
  "test:bench": "vitest bench --config vite.config.ts --no-cache --project probe",
@@ -90,30 +90,29 @@ var package_default = {
90
90
  "prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release"
91
91
  },
92
92
  dependencies: {
93
- "@orkestrel/console": "^0.0.12",
94
- "@orkestrel/contract": "^0.0.16",
95
- "@orkestrel/emitter": "^0.0.9",
96
- "@orkestrel/markdown": "^0.0.13",
97
- "@orkestrel/process": "^0.0.10",
98
- "@orkestrel/template": "^0.0.6"
93
+ "@orkestrel/console": "^0.0.13",
94
+ "@orkestrel/contract": "^0.0.17",
95
+ "@orkestrel/emitter": "^0.0.10",
96
+ "@orkestrel/markdown": "^0.0.14",
97
+ "@orkestrel/process": "^0.0.11",
98
+ "@orkestrel/template": "^0.0.7"
99
99
  },
100
100
  devDependencies: {
101
101
  "@microsoft/api-extractor": "^7.59.0",
102
- "@orkestrel/guide": "^0.0.17",
103
- "@orkestrel/html": "^0.0.8",
104
- "@orkestrel/probe": "^0.0.12",
105
- "@orkestrel/test": "^0.0.13",
106
- "@types/node": "^26.4.0",
102
+ "@orkestrel/guide": "^0.0.18",
103
+ "@orkestrel/html": "^0.0.9",
104
+ "@orkestrel/probe": "^0.0.13",
105
+ "@orkestrel/test": "^0.0.14",
106
+ "@types/node": "^26.5.0",
107
107
  "@vitest/browser-playwright": "^4.1.11",
108
- "oxfmt": "^0.65.0",
109
- "oxlint": "^1.80.0",
110
- "playwright": "^1.62.1",
108
+ "oxfmt": "^0.67.0",
109
+ "oxlint": "^1.82.0",
110
+ "playwright": "^1.63.0",
111
111
  "typescript": "^6.0.3",
112
112
  "vite": "^8.2.2",
113
- "vite-plugin-dts": "^5.0.3",
114
113
  "vitest": "^4.1.11"
115
114
  },
116
- engines: { "node": ">=22.12.0" }
115
+ engines: { "node": ">=22.18.0" }
117
116
  };
118
117
  //#endregion
119
118
  //#region src/core/constants.ts
@@ -213,10 +212,11 @@ var BIN_ENTRY_PATH = "src/bin/main.ts";
213
212
  * @remarks
214
213
  * These are the files the fleet shares verbatim, and each target holds a copy
215
214
  * of the paths it selects: the licence, the harness permission file, the
216
- * session-start hooks, the shared policy register, the shared policy proof,
217
- * the shared policy plugin, the shared configuration leaf and its proof, the
218
- * byte-identical root dotfiles, and the guide mirrors a generated workspace
219
- * starts from. A directory entry vendors everything beneath it.
215
+ * session-start hooks, the shared policy
216
+ * register, the shared policy proof, the shared policy plugin, the shared
217
+ * configuration leaf and its proof, the byte-identical root dotfiles, and the
218
+ * guide mirrors a generated workspace starts from. A directory entry vendors
219
+ * everything beneath it.
220
220
  *
221
221
  * A plan carries the subset its target selects, which is why the list is a
222
222
  * candidate set rather than a plan: a workspace never mirrors its own guide.
@@ -225,16 +225,13 @@ var BIN_ENTRY_PATH = "src/bin/main.ts";
225
225
  * its rules, its skills, its agent roles, its bench configuration, and its MCP
226
226
  * registrations from {@link CANON_PATHS} inside the installed package, so no
227
227
  * file scaffold leaves in a target names a path the target does not hold.
228
- * `nameToHostArtifacts` appends {@link CATALOG_AGENT_PATH} to what this list
228
+ * `blueprintToHostArtifacts` appends {@link CATALOG_AGENT_PATH} to what this list
229
229
  * selects, which is what keeps the list itself disjoint from the canon.
230
230
  */
231
231
  var HOST_PATHS = Object.freeze([
232
232
  "LICENSE",
233
233
  ".claude/settings.json",
234
- "scripts/deps.sh",
235
- "scripts/cursor.sh",
236
- "scripts/codex.sh",
237
- "scripts/ollama.sh",
234
+ "scripts",
238
235
  "tests/setupPolicy.ts",
239
236
  "tests/policy.test.ts",
240
237
  "tests/config.test.ts",
@@ -273,7 +270,7 @@ var HOST_PATHS = Object.freeze([
273
270
  *
274
271
  * The plan claims paths inside the canon deliberately, and each has a reason.
275
272
  * `blueprintToDocumentArtifacts` claims `AGENTS.md` and `CLAUDE.md` as this
276
- * package's own template pointers. `nameToHostArtifacts` claims
273
+ * package's own template pointers. `blueprintToHostArtifacts` claims
277
274
  * {@link CATALOG_AGENT_PATH}, because the catalog verb refuses a target that
278
275
  * lacks the file and repair restores its absence.
279
276
  *
@@ -347,19 +344,19 @@ var ORCHESTRATION_PATH_PREFIXES = Object.freeze([
347
344
  "scripts/"
348
345
  ]);
349
346
  /**
350
- * Lists the exact root filenames that wire an agent bench rather than the toolchain, frozen.
347
+ * Lists the exact root paths that wire an agent bench or own an orchestration directory, frozen.
351
348
  *
352
349
  * @remarks
353
- * `.mcp.json` registers MCP servers for the harness. It sits among the root
354
- * dotfiles but governs agents, so it groups with the harness bridges.
350
+ * `.mcp.json` registers MCP servers for the harness. `scripts` is the owned
351
+ * directory whose members wire the development environment.
355
352
  */
356
- var ORCHESTRATION_PATH_NAMES = Object.freeze([".mcp.json"]);
353
+ var ORCHESTRATION_PATH_NAMES = Object.freeze([".mcp.json", "scripts"]);
357
354
  /**
358
355
  * Names the agent file whose marker-bounded package table the catalog verb alone owns.
359
356
  *
360
357
  * @remarks
361
358
  * A plan claims it at a canon path, because the catalog verb refuses a target
362
- * that lacks the file. `nameToHostArtifacts` appends it to the vendored
359
+ * that lacks the file. `blueprintToHostArtifacts` appends it to the vendored
363
360
  * selection, and it reaches a release through the `.claude/agents` directory in
364
361
  * {@link CANON_PATHS} rather than through {@link HOST_PATHS}, which is what
365
362
  * keeps the two lists disjoint.
@@ -387,11 +384,11 @@ var CATALOG_OPENING_MARKER = "<!-- orkestrel:catalog -->";
387
384
  * refused rather than rewritten.
388
385
  */
389
386
  var CATALOG_CLOSING_MARKER = "<!-- /orkestrel:catalog -->";
390
- /** Names the provisioner skeleton a workspace with declared service vendors is given once. */
387
+ /** Names the inventory skeleton a workspace with declared service vendors is given once. */
391
388
  var SERVICE_SCRIPT_PATH = "scripts/service.sh";
392
389
  /** Names the shared Vitest global-setup module whose presence makes a workspace `global`. */
393
390
  var GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
394
- /** Names the guide-parity proof whose presence selects the planned `guides` project. */
391
+ /** Names the package-owned guide-parity entry used by `test:guides` and to select the `guides` project. */
395
392
  var GUIDES_TEST_PATH = "tests/guides.test.ts";
396
393
  /** Names the generated packed-package proof every publishing workspace is planned at. */
397
394
  var DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
@@ -530,11 +527,29 @@ var MAX_MANIFEST_BYTES = 1048576;
530
527
  /** Caps the bytes retained across one whole plan or audit. */
531
528
  var MAX_TOTAL_ARTIFACT_BYTES = 104857600;
532
529
  /** Names the oldest Node version the generated toolchain supports. */
533
- var MINIMUM_NODE_VERSION = "22.12.0";
530
+ var MINIMUM_NODE_VERSION = "22.18.0";
531
+ /** Names the oldest npm version the generated toolchain supports. */
532
+ var MINIMUM_NPM_VERSION = "11.6.0";
534
533
  /** Names the version a workspace starts at. */
535
534
  var DEFAULT_VERSION = "0.0.1";
536
535
  /** Names the `engines.node` range a workspace starts with. */
537
536
  var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
537
+ /**
538
+ * Holds the `devEngines` record every generated manifest carries.
539
+ *
540
+ * @remarks
541
+ * Every generated manifest names npm at the {@link MINIMUM_NPM_VERSION} floor with the `onFail`
542
+ * key set to the `error` value, and no blueprint field varies that record. An npm at 10.9.0 or
543
+ * later reads the `devEngines` record. Such an npm earlier than the floor refuses an install in a
544
+ * generated workspace rather than resolving its dependency graph.
545
+ * The neighbouring `DEFAULT_ENGINES` constant is the Node range, and a blueprint's `engines`
546
+ * field does replace that one.
547
+ */
548
+ var WORKSPACE_DEV_ENGINES = Object.freeze({ packageManager: Object.freeze({
549
+ name: "npm",
550
+ version: `>=${MINIMUM_NPM_VERSION}`,
551
+ onFail: "error"
552
+ }) });
538
553
  /** Holds the tooling versions scaffold and every generated workspace share. */
539
554
  var BASE_DEV_DEPENDENCIES = Object.freeze({
540
555
  "@orkestrel/guide": package_default.devDependencies["@orkestrel/guide"],
@@ -549,13 +564,13 @@ var BASE_DEV_DEPENDENCIES = Object.freeze({
549
564
  vitest: package_default.devDependencies.vitest
550
565
  });
551
566
  /**
552
- * Lists the development dependencies that emit declarations for published source or an
553
- * executable.
567
+ * Lists the development dependencies that roll declarations up for published source.
568
+ *
569
+ * @remarks
570
+ * The toolchain runs the compiler it already installs as a command, emitting one declaration per
571
+ * module, and the extractor rolls that emit into the single file each published face ships.
554
572
  */
555
- var DECLARATION_DEV_DEPENDENCIES = Object.freeze({
556
- "@microsoft/api-extractor": package_default.devDependencies["@microsoft/api-extractor"],
557
- "vite-plugin-dts": package_default.devDependencies["vite-plugin-dts"]
558
- });
573
+ var DECLARATION_DEV_DEPENDENCIES = Object.freeze({ "@microsoft/api-extractor": package_default.devDependencies["@microsoft/api-extractor"] });
559
574
  /** Lists the development dependencies a published browser `src` environment adds. */
560
575
  var SOURCE_BROWSER_DEV_DEPENDENCIES = Object.freeze({
561
576
  "@vitest/browser-playwright": package_default.devDependencies["@vitest/browser-playwright"],
@@ -896,9 +911,10 @@ export function appBrowser(): UserConfig {
896
911
  setupFiles: ['./tests/setup.ts'],
897
912
  environment: 'node',
898
913
  browser: { enabled: false },
899
- // A config test validates every target wrapper and runs the real linter twice with
900
- // 15-second child caps, so this budget clears both caps and reports their diagnostics.
901
- testTimeout: 45_000,
914
+ // A config test validates every target wrapper, spawns the real linter twice under
915
+ // 15-second child caps, and rolls one face up through the compiler and the extractor it
916
+ // spawns, so this budget clears the capped pair with room for a contended host.
917
+ testTimeout: 60_000,
902
918
  },
903
919
  })
904
920
  `,
@@ -938,9 +954,9 @@ export const conformance = (): UserConfig => ({
938
954
  },
939
955
  })
940
956
  `,
941
- service: `// The live external services this package drives. It starts nothing itself:
942
- // \`scripts/service.sh\` provisions, \`tests/setupService.ts\` proves readiness, and
943
- // the project stays out of \`npm test\` because a real service answers it.
957
+ service: `// The caller prepares the live external services before this project.
958
+ // \`tests/setupService.ts\` verifies readiness, and the project stays out of \`npm test\`
959
+ // because a real service answers it.
944
960
  export const service = (): UserConfig => ({
945
961
  resolve,
946
962
  test: {
@@ -1124,8 +1140,7 @@ export const probe = (): UserConfig => ({
1124
1140
  vites: Object.freeze({
1125
1141
  src: Object.freeze({
1126
1142
  core: `import { defineConfig, mergeConfig } from 'vite'
1127
- import dts from 'vite-plugin-dts'
1128
- import { environmentBoundary, outputBoundary } from '../helpers.js'
1143
+ import { declarationRollup, environmentBoundary, outputBoundary } from '../helpers.js'
1129
1144
  import { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'
1130
1145
 
1131
1146
  export default defineConfig(
@@ -1134,17 +1149,9 @@ export default defineConfig(
1134
1149
  plugins: [
1135
1150
  outputBoundary('dist/src/core'),
1136
1151
  environmentBoundary('src/core'),
1137
- dts({
1138
- tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.core.json'),
1139
- bundleTypes: {
1140
- extractorConfig: {
1141
- compiler: {
1142
- overrideTsconfig: {
1143
- compilerOptions: { types: ['node'] },
1144
- },
1145
- },
1146
- },
1147
- },
1152
+ declarationRollup({
1153
+ project: resolveWorkspacePath('configs/src/tsconfig.core.json'),
1154
+ types: ['node'],
1148
1155
  }),
1149
1156
  ],
1150
1157
  build: {
@@ -1165,49 +1172,34 @@ export default defineConfig(
1165
1172
  )
1166
1173
  `,
1167
1174
  browser: `import { defineConfig, mergeConfig } from 'vite'
1168
- import dts from 'vite-plugin-dts'
1175
+ import { declarationRollup, rewriteCoreSpecifier } from '../helpers.js'
1169
1176
  import { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'
1170
1177
 
1171
- // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1172
- // src/core through a relative source path the tarball does not carry. The path
1173
- // keeps each source module's own depth, so a module in a browser subfolder emits
1174
- // one that leaves dist/src entirely. The following rewrite externalizes core
1175
- // through the package's own published root export, on the final roll-up only.
1178
+ // The roll-up reaches src/core through a specifier the tarball does not carry, so the rewrite
1179
+ // externalizes core through the package's own published root export, on the final roll-up alone.
1176
1180
  export default defineConfig(
1177
1181
  mergeConfig(srcBrowser(), {
1178
1182
  plugins: [
1179
- dts({
1180
- tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.browser.json'),
1181
- bundleTypes: true,
1182
- beforeWriteFile: (path, content) => ({
1183
- content: /[\\\\/]dist[\\\\/]src[\\\\/]browser[\\\\/]index\\.d\\.ts$/.test(path)
1184
- {{replacement}}
1185
- : content,
1186
- }),
1183
+ declarationRollup({
1184
+ project: resolveWorkspacePath('configs/src/tsconfig.browser.json'),
1185
+ rewrite: rewriteCoreSpecifier,
1187
1186
  }),
1188
1187
  ],
1189
1188
  }),
1190
1189
  )
1191
1190
  `,
1192
1191
  server: `import { defineConfig, mergeConfig } from 'vite'
1193
- import dts from 'vite-plugin-dts'
1192
+ import { declarationRollup, rewriteCoreSpecifier } from '../helpers.js'
1194
1193
  import { srcServer, resolveWorkspacePath } from '../../vite.config.ts'
1195
1194
 
1196
- // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1197
- // src/core through a relative source path the tarball does not carry. The
1198
- // following rewrite externalizes core through the package's own published root
1199
- // export, on the final roll-up only.
1195
+ // The roll-up reaches src/core through a specifier the tarball does not carry, so the rewrite
1196
+ // externalizes core through the package's own published root export, on the final roll-up alone.
1200
1197
  export default defineConfig(
1201
1198
  mergeConfig(srcServer(), {
1202
1199
  plugins: [
1203
- dts({
1204
- tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.server.json'),
1205
- bundleTypes: true,
1206
- beforeWriteFile: (path, content) => ({
1207
- content: /[\\\\/]dist[\\\\/]src[\\\\/]server[\\\\/]index\\.d\\.ts$/.test(path)
1208
- {{replacement}}
1209
- : content,
1210
- }),
1200
+ declarationRollup({
1201
+ project: resolveWorkspacePath('configs/src/tsconfig.server.json'),
1202
+ rewrite: rewriteCoreSpecifier,
1211
1203
  }),
1212
1204
  ],
1213
1205
  }),
@@ -1644,14 +1636,20 @@ import {
1644
1636
  statSync,
1645
1637
  writeFileSync,
1646
1638
  } from 'node:fs'
1647
- {{transport}}import { tmpdir } from 'node:os'
1639
+ {{transport}}import { createRequire } from 'node:module'
1640
+ import { tmpdir } from 'node:os'
1648
1641
  import { dirname, join, resolve } from 'node:path'
1649
1642
  import { fileURLToPath } from 'node:url'
1650
- {{launcher}}import ts from 'typescript'
1651
- import { afterAll, describe, expect, it } from 'vitest'
1643
+ {{launcher}}import { afterAll, describe, expect, it } from 'vitest'
1652
1644
 
1653
1645
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
1654
1646
  const NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'
1647
+ // The compiler this workspace installs, run as a command rather than called in
1648
+ // process: the command and its plain-text diagnostics are the same across the
1649
+ // compiler majors this toolchain supports, and its in-process API is not. It is
1650
+ // resolved from the workspace under proof, so a consumer of the packed artifact is
1651
+ // checked by the same compiler that workspace's own \`check\` script runs.
1652
+ const TSC = createRequire(join(ROOT, 'package.json')).resolve('typescript/bin/tsc')
1655
1653
  // Windows needs a shell to launch a \`.cmd\`: Node refuses one directly since the
1656
1654
  // batch-argument hardening, and \`spawnSync\` returns \`EINVAL\` with a null status
1657
1655
  // rather than an exit code a caller can read. Every following argument is a literal or
@@ -1668,6 +1666,12 @@ const RELEASE = import.meta.env.MODE === 'release'
1668
1666
  // Node and the miss is silent.
1669
1667
  const BROWSER_OUTPUT = './dist/src/browser/'
1670
1668
  const ABSENT_SUBPATH = '/no-subpath-is-published-under-this-name'
1669
+ // A compiler diagnostic that says where it is: the path relative to the directory
1670
+ // the compiler ran in, the 1-based line and column, the code, and the message. The
1671
+ // diagnostics are the verdict rather than the exit code, which differs between the
1672
+ // compiler majors this toolchain supports, so a reported line matching nothing here
1673
+ // came from something other than a check of a consumer module.
1674
+ const DIAGNOSTIC_PATTERN = /^(.+?)\\(\\d+,\\d+\\): error TS\\d+: /u
1671
1675
  const PING = ['ping', '--fetch-retries=0', '--fetch-timeout=5000', '--loglevel=silent']
1672
1676
  const ESM_DRIVER = 'drive.mjs'
1673
1677
  const CJS_DRIVER = 'drive.cjs'
@@ -1713,8 +1717,8 @@ const DECLARATION_CONDITIONS = Object.freeze({
1713
1717
 
1714
1718
  interface Resolution {
1715
1719
  readonly label: string
1716
- readonly resolution: ts.ModuleResolutionKind
1717
- readonly module: ts.ModuleKind
1720
+ readonly resolution: string
1721
+ readonly module: string
1718
1722
  readonly conditions: Readonly<Record<Format, readonly string[]>>
1719
1723
  }
1720
1724
 
@@ -1722,51 +1726,61 @@ interface TargetResolution {
1722
1726
  readonly target: string
1723
1727
  }
1724
1728
 
1725
- // Each compile driver carries the conditions TypeScript applies for its resolution
1726
- // and importing format. A \`require\`-only subpath therefore stays in each CommonJS
1727
- // probe that can resolve it.
1729
+ // Each compile driver carries the compiler options its scratch project sets and
1730
+ // the conditions TypeScript applies for that resolution and importing format. A
1731
+ // \`require\`-only subpath therefore stays in each CommonJS probe that can resolve it.
1732
+ // The option values are the spellings the project file takes, so nothing here needs
1733
+ // the compiler's own API to name them.
1728
1734
  const RESOLUTIONS: readonly Resolution[] = [
1729
1735
  {
1730
1736
  label: 'node16',
1731
- resolution: ts.ModuleResolutionKind.Node16,
1732
- module: ts.ModuleKind.Node16,
1737
+ resolution: 'node16',
1738
+ module: 'node16',
1733
1739
  conditions: DECLARATION_CONDITIONS,
1734
1740
  },
1735
1741
  {
1736
1742
  label: 'nodenext',
1737
- resolution: ts.ModuleResolutionKind.NodeNext,
1738
- module: ts.ModuleKind.NodeNext,
1743
+ resolution: 'nodenext',
1744
+ module: 'nodenext',
1739
1745
  conditions: DECLARATION_CONDITIONS,
1740
1746
  },
1741
1747
  {
1742
1748
  label: 'bundler',
1743
- resolution: ts.ModuleResolutionKind.Bundler,
1744
- module: ts.ModuleKind.ESNext,
1749
+ resolution: 'bundler',
1750
+ module: 'esnext',
1745
1751
  conditions: BUNDLER_CONDITIONS,
1746
1752
  },
1747
1753
  ]
1748
1754
 
1755
+ // The driver a bundled consumer reads declarations under. A browser application
1756
+ // compiles through a bundler, so the browser drive answers under this one alone,
1757
+ // and naming it here is what keeps that selection tied to the driver it selects.
1758
+ const BROWSER_DRIVER = RESOLUTIONS.find((candidate) => candidate.label === 'bundler')
1759
+ if (BROWSER_DRIVER === undefined) throw new Error("RESOLUTIONS carries no 'bundler' row")
1760
+
1749
1761
  const FORMATS: ReadonlyArray<readonly [extension: string, format: Format]> = [
1750
1762
  ['ts', 'module'],
1751
1763
  ['cts', 'commonjs'],
1752
1764
  ]
1753
1765
 
1754
1766
  // One published subpath, resolved to what this proof can drive: the specifier a
1755
- // consumer writes, the declarations its consumer formats name, whether its target
1756
- // is a browser bundle, and whether it answers \`import\` and \`require\` at all.
1767
+ // consumer writes, whether the declarations its consumer formats resolve at all,
1768
+ // whether the exports map answers the browser condition with a target of its own,
1769
+ // whether it answers \`import\` and \`require\` at all, and whether the target that
1770
+ // \`require\` answers with is one that a CommonJS consumer loads.
1757
1771
  interface Entry {
1758
1772
  readonly subpath: string
1759
1773
  readonly specifier: string
1760
1774
  readonly mapping: unknown
1761
1775
  readonly declaration: {
1762
- readonly module: string | undefined
1763
- readonly commonjs: string | undefined
1764
- readonly browser: string | undefined
1776
+ readonly importable: boolean
1777
+ readonly requirable: boolean
1778
+ readonly browsable: boolean
1765
1779
  }
1766
- readonly browser: boolean
1767
- readonly module: boolean
1768
- readonly commonjs: boolean
1769
- readonly required: boolean
1780
+ readonly browsable: boolean
1781
+ readonly importable: boolean
1782
+ readonly requirable: boolean
1783
+ readonly loadable: boolean
1770
1784
  }
1771
1785
 
1772
1786
  // The installed tree every claim is read from. Every subpath the exports map names
@@ -2019,7 +2033,14 @@ function isDeclaration(target: string): boolean {
2019
2033
  // against. Each field uses the conditions of the TypeScript consumer paired with
2020
2034
  // that runtime. A JavaScript target resolves through TypeScript's adjacent
2021
2035
  // declaration substitution rather than standing in for the declaration itself.
2022
- function readDeclaration(entry: unknown, installed: string): Entry['declaration'] {
2036
+ function readDeclaration(
2037
+ entry: unknown,
2038
+ installed: string,
2039
+ ): {
2040
+ readonly module: string | undefined
2041
+ readonly commonjs: string | undefined
2042
+ readonly browser: string | undefined
2043
+ } {
2023
2044
  return {
2024
2045
  module: resolveDeclaration(entry, DECLARATION_CONDITIONS.module, installed),
2025
2046
  commonjs: resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed),
@@ -2032,72 +2053,137 @@ function selectEntries(entries: readonly Entry[], conditions: readonly string[])
2032
2053
  return entries.filter(
2033
2054
  (entry) =>
2034
2055
  resolveTarget(entry.mapping, conditions) !== undefined &&
2035
- (!conditions.includes('require') || entry.commonjs),
2056
+ (!conditions.includes('require') || entry.loadable),
2057
+ )
2058
+ }
2059
+
2060
+ // The compile drivers whose conditions reach one entry under one importing format.
2061
+ // A driver that resolves no target for that entry compiles nothing, so a consumer
2062
+ // written under it would report a resolution failure this package never made.
2063
+ function selectDrivers(entry: Entry, format: Format): readonly Resolution[] {
2064
+ return RESOLUTIONS.filter(
2065
+ (driver) => selectEntries([entry], driver.conditions[format]).length > 0,
2036
2066
  )
2037
2067
  }
2038
2068
 
2039
- // Require-loadable entries that declare CommonJS support but a typed CommonJS
2040
- // consumer cannot compile against. A default branch resolving under the require
2041
- // condition set makes no CommonJS claim.
2069
+ // Requirable entries that declare CommonJS support but a typed CommonJS consumer
2070
+ // cannot compile against. A default branch resolving under the require condition
2071
+ // set makes no CommonJS claim.
2042
2072
  function selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {
2043
2073
  return entries.filter(
2044
2074
  (entry) =>
2045
- entry.required &&
2075
+ entry.requirable &&
2046
2076
  isRecord(entry.mapping) &&
2047
2077
  Object.hasOwn(entry.mapping, 'require') &&
2048
2078
  !declaresCommonJS(entry.mapping, installed),
2049
2079
  )
2050
2080
  }
2051
2081
 
2052
- // The value exports a declaration publishes, read through the compiler's checker
2053
- // over the module symbol rather than off the declaration text. An alias resolves to
2054
- // what it names, so a re-export counts as the thing it re-exports, and a type-only
2055
- // symbol is dropped because no runtime publishes one.
2056
- function readDeclaredExports(declaration: string): readonly string[] {
2057
- const program = ts.createProgram([declaration], {
2058
- module: ts.ModuleKind.ESNext,
2059
- moduleResolution: ts.ModuleResolutionKind.Bundler,
2060
- noEmit: true,
2061
- skipLibCheck: true,
2062
- target: ts.ScriptTarget.ESNext,
2063
- })
2064
- const source = program.getSourceFile(declaration)
2065
- if (source === undefined) throw new Error(\`The declaration \${declaration} was not read\`)
2066
- const checker = program.getTypeChecker()
2067
- const symbol = checker.getSymbolAtLocation(source)
2068
- if (symbol === undefined) throw new Error(\`\${declaration} declares no module symbol\`)
2069
- const values: string[] = []
2070
- for (const exported of checker.getExportsOfModule(symbol)) {
2071
- const direct = (exported.flags & ts.SymbolFlags.Alias) === 0
2072
- const resolved = direct ? exported : checker.getAliasedSymbol(exported)
2073
- if ((resolved.flags & ts.SymbolFlags.Value) !== 0) values.push(exported.getName())
2082
+ // One surface comparison, written as the consumer module that proves it: the
2083
+ // installed entry that consumer imports, the file extension fixing its importing
2084
+ // format, the names a real runtime published off it, and the driver whose scratch
2085
+ // project compiles it.
2086
+ interface Surface {
2087
+ readonly entry: Entry
2088
+ readonly extension: string
2089
+ readonly published: readonly string[]
2090
+ readonly driver: Resolution
2091
+ }
2092
+
2093
+ // A scratch project over named consumer modules, written beside them so their own
2094
+ // resolution reaches the installed package. Nothing is emitted and no ambient types
2095
+ // are pulled in, so what the check reads is the installed declarations alone.
2096
+ function writeProject(
2097
+ stage: Stage,
2098
+ name: string,
2099
+ driver: Resolution,
2100
+ files: readonly string[],
2101
+ ): string {
2102
+ const path = join(stage.consumer, \`tsconfig.\${name}.json\`)
2103
+ const project = {
2104
+ compilerOptions: {
2105
+ module: driver.module,
2106
+ moduleResolution: driver.resolution,
2107
+ noEmit: true,
2108
+ skipLibCheck: true,
2109
+ strict: true,
2110
+ target: 'esnext',
2111
+ types: [],
2112
+ },
2113
+ files: [...files],
2074
2114
  }
2075
- return [...values].sort()
2115
+ writeFile(path, \`\${JSON.stringify(project, undefined, '\\t')}\\n\`)
2116
+ return path
2076
2117
  }
2077
2118
 
2078
- // The diagnostics a consumer compiling against the installed declarations reports,
2079
- // flattened to their messages so a failure names what the consumer could not do.
2080
- function compileConsumer(
2081
- entry: string,
2082
- resolution: ts.ModuleResolutionKind,
2083
- module: ts.ModuleKind,
2084
- ): readonly string[] {
2085
- const program = ts.createProgram([entry], {
2086
- module,
2087
- moduleResolution: resolution,
2088
- noEmit: true,
2089
- skipLibCheck: true,
2090
- strict: true,
2091
- target: ts.ScriptTarget.ESNext,
2092
- })
2093
- return ts
2094
- .getPreEmitDiagnostics(program)
2095
- .map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, ' '))
2119
+ // The diagnostics the compiler this workspace installs reports for one scratch
2120
+ // project. The compiler runs as a command, so nothing here reaches an API that
2121
+ // moves between its majors, and the located lines it prints are the verdict rather
2122
+ // than the exit code, which moves between them. A line carrying no location, a line
2123
+ // naming the scratch project rather than a consumer module, and anything at all on
2124
+ // the error stream are faults of this proof rather than of the package under proof,
2125
+ // so each is raised where it happens instead of counted against the package.
2126
+ function checkProject(stage: Stage, project: string): readonly string[] {
2127
+ const result = runNode([TSC, '--noEmit', '--pretty', 'false', '-p', project], stage.consumer)
2128
+ const refused = \`\${result.stderr ?? ''}\`.trim()
2129
+ if (refused.length > 0) {
2130
+ throw new Error(\`The consumer compiler wrote \${refused} to its error stream\`)
2131
+ }
2132
+ const reported: string[] = []
2133
+ for (const line of \`\${result.stdout ?? ''}\`.split(/\\r\\n|\\n/u)) {
2134
+ if (line.trim().length === 0) continue
2135
+ const last = reported.at(-1)
2136
+ // An elaborated diagnostic prints its detail on indented lines under its own
2137
+ // first line, so each of those joins the diagnostic it elaborates.
2138
+ if (/^\\s/u.test(line) && last !== undefined) {
2139
+ reported[reported.length - 1] = \`\${last} \${line.trim()}\`
2140
+ continue
2141
+ }
2142
+ const located = DIAGNOSTIC_PATTERN.exec(line)?.[1]
2143
+ if (located === undefined) {
2144
+ throw new Error(\`The consumer compiler reported \${line}, which names no location\`)
2145
+ }
2146
+ if (resolve(stage.consumer, located) === project) {
2147
+ throw new Error(\`The scratch project is itself at fault: \${line}\`)
2148
+ }
2149
+ reported.push(line)
2150
+ }
2151
+ if (reported.length === 0 && result.status !== 0) {
2152
+ throw new Error(\`The consumer compiler refused the project: \${readOutput(result)}\`)
2153
+ }
2154
+ return reported
2155
+ }
2156
+
2157
+ // One installed entry's published names checked against its own declarations by
2158
+ // the compiler this workspace installs, in the direction each divergence surfaces
2159
+ // under. The runtime's key list is written into the consumer as a literal, so the
2160
+ // published side comes from a real process and the declared side from the
2161
+ // declarations that process's package ships, and the two can disagree. A name the
2162
+ // declarations carry and the runtime does not lands on \`declared\`. A name the
2163
+ // runtime carries and the declarations do not, and a name the declarations publish
2164
+ // as a type alone, land on \`surfaced\`: a variable widens into the type
2165
+ // \`declared\` annotates, and only \`surfaced\` reads the literal's own keys back.
2166
+ // Each names the member it is about, so the failure says which export moved.
2167
+ function checkSurface(stage: Stage, surface: Surface): readonly string[] {
2168
+ const slug = surface.entry.subpath.replaceAll(/[^\\w]+/gu, '-')
2169
+ const name = \`surface.\${surface.driver.label}\${slug}.\${surface.extension}\`
2170
+ const module = \`\${name}\`
2171
+ const keys = surface.published.map((key) => \`\${JSON.stringify(key)}: true\`).join(', ')
2172
+ writeFile(
2173
+ join(stage.consumer, module),
2174
+ \`import * as entry from \${JSON.stringify(surface.entry.specifier)}
2175
+ const published = {\${keys.length === 0 ? '' : \` \${keys} \`}} as const
2176
+ const declared: Record<keyof typeof entry, true> = published
2177
+ const surfaced: Record<keyof typeof published, true> = declared
2178
+ \`,
2179
+ )
2180
+ const project = writeProject(stage, name, surface.driver, [\`./\${module}\`])
2181
+ return checkProject(stage, project).map((line) => \`\${surface.driver.label}: \${line}\`)
2096
2182
  }
2097
2183
 
2098
2184
  // One consumer module importing every installed entry, written where its own
2099
2185
  // resolution finds the installed package.
2100
- function writeConsumerProbe(stage: Stage, path: string, specifiers: readonly string[]): string {
2186
+ function writeConsumerProbe(stage: Stage, path: string, specifiers: readonly string[]): void {
2101
2187
  const names: string[] = []
2102
2188
  const bindings: string[] = []
2103
2189
  for (const [index, specifier] of specifiers.entries()) {
@@ -2105,9 +2191,8 @@ function writeConsumerProbe(stage: Stage, path: string, specifiers: readonly str
2105
2191
  names.push(binding)
2106
2192
  bindings.push(\`import * as \${binding} from \${JSON.stringify(specifier)}\`)
2107
2193
  }
2108
- const target = join(stage.consumer, path)
2109
- writeFile(target, \`\${bindings.join('\\n')}\\nexport const surface = [\${names.join(', ')}]\\n\`)
2110
- return target
2194
+ const source = \`\${bindings.join('\\n')}\\nexport const surface = [\${names.join(', ')}]\\n\`
2195
+ writeFile(join(stage.consumer, path), source)
2111
2196
  }
2112
2197
 
2113
2198
  // The runtime key set a real process reads off one installed entry under one
@@ -2177,27 +2262,26 @@ function buildStage(): Stage {
2177
2262
  else excluded.push(subpath)
2178
2263
  continue
2179
2264
  }
2180
- const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
2181
- const requiredTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
2265
+ const importTarget = resolveTarget(entry, RUNTIME_CONDITIONS.module)
2266
+ const requireTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
2182
2267
  const browserTarget = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2183
- const browser = resolvesBrowser(entry)
2184
- const required = requiredTarget !== undefined && !(browser && requiredTarget === browserTarget)
2185
- const commonjs = required && resolvesCommonJS(entry, installed)
2268
+ const browsable = resolvesBrowser(entry)
2269
+ const shadowed = browsable && requireTarget === browserTarget
2270
+ const requirable = requireTarget !== undefined && !shadowed
2271
+ const loadable = requirable && resolvesCommonJS(entry, installed)
2186
2272
  entries.push({
2187
2273
  subpath,
2188
2274
  specifier: subpath === '.' ? name : \`\${name}\${subpath.slice(1)}\`,
2189
2275
  mapping: entry,
2190
2276
  declaration: {
2191
- module: declaration.module === undefined ? undefined : join(installed, declaration.module),
2192
- commonjs:
2193
- declaration.commonjs === undefined ? undefined : join(installed, declaration.commonjs),
2194
- browser:
2195
- declaration.browser === undefined ? undefined : join(installed, declaration.browser),
2277
+ importable: declaration.module !== undefined,
2278
+ requirable: declaration.commonjs !== undefined,
2279
+ browsable: declaration.browser !== undefined,
2196
2280
  },
2197
- browser,
2198
- module: imported !== undefined && !(browser && imported === browserTarget),
2199
- commonjs,
2200
- required,
2281
+ browsable,
2282
+ importable: importTarget !== undefined && !(browsable && importTarget === browserTarget),
2283
+ requirable,
2284
+ loadable,
2201
2285
  })
2202
2286
  }
2203
2287
  return { consumer, installed, archives, entries, subpaths, undeclared, excluded, targets }
@@ -2348,7 +2432,7 @@ describe('installed package consumer', () => {
2348
2432
  // loads it. Each later drive retires itself for that entry, so this assertion names
2349
2433
  // the subpath rather than counting it as driven.
2350
2434
  const unreachable = stage.entries.filter(
2351
- (entry) => !entry.module && !entry.required && !entry.browser,
2435
+ (entry) => !entry.importable && !entry.requirable && !entry.browsable,
2352
2436
  )
2353
2437
  expect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])
2354
2438
  const untypable = selectUntypable(stage.entries, stage.installed)
@@ -2377,16 +2461,18 @@ describe('installed package consumer', () => {
2377
2461
  for (const [extension, format] of FORMATS) {
2378
2462
  const written = selectEntries(stage.entries, driver.conditions[format])
2379
2463
  if (written.length === 0) continue
2464
+ const label = \`\${driver.label}.\${extension}\`
2465
+ const probe = \`probe.\${label}\`
2380
2466
  const specifiers = written.map((entry) => entry.specifier)
2381
- const probe = writeConsumerProbe(stage, \`probe.\${driver.label}.\${extension}\`, specifiers)
2382
- for (const message of compileConsumer(probe, driver.resolution, driver.module)) {
2383
- reported.push(\`\${driver.label}.\${extension}: \${message}\`)
2384
- }
2385
- const absent = [\`\${name}\${ABSENT_SUBPATH}\`]
2386
- const control = writeConsumerProbe(stage, \`control.\${driver.label}.\${extension}\`, absent)
2387
- if (compileConsumer(control, driver.resolution, driver.module).length === 0) {
2388
- silent.push(\`\${driver.label}.\${extension}\`)
2467
+ writeConsumerProbe(stage, probe, specifiers)
2468
+ const project = writeProject(stage, probe, driver, [\`./\${probe}\`])
2469
+ for (const message of checkProject(stage, project)) {
2470
+ reported.push(\`\${label}: \${message}\`)
2389
2471
  }
2472
+ const control = \`control.\${label}\`
2473
+ writeConsumerProbe(stage, control, [\`\${name}\${ABSENT_SUBPATH}\`])
2474
+ const refused = writeProject(stage, control, driver, [\`./\${control}\`])
2475
+ if (checkProject(stage, refused).length === 0) silent.push(label)
2390
2476
  }
2391
2477
  }
2392
2478
  expect(reported).toStrictEqual([])
@@ -2396,27 +2482,44 @@ describe('installed package consumer', () => {
2396
2482
 
2397
2483
  for (const entry of STAGE?.entries ?? []) {
2398
2484
  describe(\`installed entry \${entry.subpath}\`, () => {
2399
- it.runIf(entry.module)(
2485
+ it.runIf(entry.importable)(
2400
2486
  'publishes what it declares to a Node import, and no more',
2401
2487
  (context) => {
2402
- const declaration = entry.declaration.module
2403
- if (declaration === undefined) {
2488
+ const stage = requireStage(context)
2489
+ // The exports-map walk resolved a declaration a typed importer reads, so an
2490
+ // entry reaching this drive without one is reported for that rather than for
2491
+ // what a consumer of a missing declaration goes on to say.
2492
+ if (!entry.declaration.importable) {
2404
2493
  throw new Error(\`\${entry.subpath} publishes no import declaration\`)
2405
2494
  }
2406
- const published = driveRuntime(requireStage(context), entry.specifier, ESM_DRIVER)
2407
- expect(published).toStrictEqual(readDeclaredExports(declaration))
2495
+ const published = driveRuntime(stage, entry.specifier, ESM_DRIVER)
2496
+ const drivers = selectDrivers(entry, 'module')
2497
+ expect(drivers).not.toStrictEqual([])
2498
+ const reported = drivers.flatMap((driver) =>
2499
+ checkSurface(stage, { entry, extension: 'ts', published, driver }),
2500
+ )
2501
+ expect(reported).toStrictEqual([])
2408
2502
  },
2409
2503
  )
2410
2504
 
2411
- it.runIf(entry.required)(
2505
+ it.runIf(entry.requirable)(
2412
2506
  'publishes what it declares to a Node require, and no more',
2413
2507
  (context) => {
2414
- const declaration = entry.declaration.commonjs
2415
- if (declaration === undefined) {
2508
+ const stage = requireStage(context)
2509
+ if (!entry.declaration.requirable) {
2416
2510
  throw new Error(\`\${entry.subpath} publishes no require declaration\`)
2417
2511
  }
2418
- const published = driveRuntime(requireStage(context), entry.specifier, CJS_DRIVER)
2419
- expect(published).toStrictEqual(readDeclaredExports(declaration))
2512
+ const published = driveRuntime(stage, entry.specifier, CJS_DRIVER)
2513
+ // A subpath whose \`require\` resolves to a module that no typed CommonJS
2514
+ // consumer can compile against carries no declared side to compare here, and
2515
+ // whether it may publish one at all is the untypable set's question rather
2516
+ // than this drive's. The preceding runtime drive ran either way.
2517
+ const drivers = selectDrivers(entry, 'commonjs')
2518
+ expect(drivers).not.toStrictEqual([])
2519
+ const reported = drivers.flatMap((driver) =>
2520
+ checkSurface(stage, { entry, extension: 'cts', published, driver }),
2521
+ )
2522
+ expect(reported).toStrictEqual([])
2420
2523
  },
2421
2524
  )
2422
2525
  {{drive}} })
@@ -2529,12 +2632,11 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
2529
2632
  }
2530
2633
  `,
2531
2634
  drive: `
2532
- it.runIf(entry.browser)(
2635
+ it.runIf(entry.browsable)(
2533
2636
  'publishes what it declares to a real browser, and no more [requires a browser]',
2534
2637
  async (context) => {
2535
2638
  const stage = requireStage(context)
2536
- const declaration = entry.declaration.browser
2537
- if (declaration === undefined) {
2639
+ if (!entry.declaration.browsable) {
2538
2640
  throw new Error(\`\${entry.subpath} publishes no browser declaration\`)
2539
2641
  }
2540
2642
  const options = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
@@ -2545,9 +2647,16 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
2545
2647
  })
2546
2648
  try {
2547
2649
  const bundle = await bundleEntry(stage, entry)
2548
- expect(await readBrowserExports(browser, bundle)).toStrictEqual(
2549
- readDeclaredExports(declaration),
2550
- )
2650
+ const published = await readBrowserExports(browser, bundle)
2651
+ // A browser consumer reads the installed declarations through a bundler, so
2652
+ // that is the one driver this face answers under.
2653
+ const reported = checkSurface(stage, {
2654
+ entry,
2655
+ extension: 'ts',
2656
+ published,
2657
+ driver: BROWSER_DRIVER,
2658
+ })
2659
+ expect(reported).toStrictEqual([])
2551
2660
  } finally {
2552
2661
  await browser.close()
2553
2662
  }
@@ -2570,7 +2679,7 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
2570
2679
  // file and runs the \`repair\` verb, which writes the variant carrying that branch.
2571
2680
  it('publishes no browser face this proof cannot drive [requires the registry]', (context) => {
2572
2681
  const stage = requireStage(context)
2573
- const faces = stage.entries.filter((entry) => entry.browser)
2682
+ const faces = stage.entries.filter((entry) => entry.browsable)
2574
2683
  expect(faces.map((entry) => entry.subpath)).toStrictEqual([])
2575
2684
  })
2576
2685
  `
@@ -2685,12 +2794,12 @@ printf '%s\\n' \\
2685
2794
  *
2686
2795
  * @example
2687
2796
  * ```ts
2688
- * import { ScaffoldError, isScaffoldError } from '@orkestrel/scaffold'
2797
+ * import { isScaffoldError, ScaffoldError } from '@orkestrel/scaffold'
2689
2798
  *
2690
2799
  * try {
2691
- * throw new ScaffoldError('INVALID', 'Blueprint is not an exact record')
2800
+ * throw new ScaffoldError('TARGET', 'The target carries no readable manifest.')
2692
2801
  * } catch (error) {
2693
- * if (isScaffoldError(error)) error.code // 'INVALID'
2802
+ * if (isScaffoldError(error)) error.code // 'TARGET'
2694
2803
  * }
2695
2804
  * ```
2696
2805
  */
@@ -3135,7 +3244,8 @@ var isCatalogEntry = (0, _orkestrel_contract.unionOf)((0, _orkestrel_contract.re
3135
3244
  name: isDependencyName,
3136
3245
  lookup: (0, _orkestrel_contract.literalOf)("found"),
3137
3246
  version: _orkestrel_contract.isString,
3138
- dependencies: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency))
3247
+ dependencies: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency)),
3248
+ peers: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency))
3139
3249
  }), (0, _orkestrel_contract.recordOf)({
3140
3250
  name: isDependencyName,
3141
3251
  lookup: (0, _orkestrel_contract.literalOf)("missing", "unmatched", "failed"),
@@ -3521,7 +3631,7 @@ function isCanonPath(path) {
3521
3631
  return CANON_PATHS.some((canon) => path === canon || path.startsWith(`${canon}/`));
3522
3632
  }
3523
3633
  /**
3524
- * Checks whether a target's present bytes at a path are owned by another surface.
3634
+ * Checks whether another surface owns a target's present bytes at a path.
3525
3635
  *
3526
3636
  * @param path - The target-relative path to test.
3527
3637
  * @returns True if the path is a {@link WORKSPACE_OWNED_PATHS} member or a
@@ -3698,46 +3808,6 @@ function matchesPrintWidth(line) {
3698
3808
  return line.replaceAll(" ", " ".repeat(2)).length <= 100;
3699
3809
  }
3700
3810
  /**
3701
- * Derives the declaration rewrite a published face's `beforeWriteFile` applies.
3702
- *
3703
- * @param name - The workspace's own bare package name.
3704
- * @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
3705
- * fills its `{{replacement}}` span with, indented for that span.
3706
- *
3707
- * @remarks
3708
- * `vite-plugin-dts` rolls a face into one declaration and keeps each source
3709
- * module's own relative depth, so a nested module emits a path that escapes
3710
- * `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
3711
- * relative core path to the package's published root export, so the branch is
3712
- * derived once here. The extension alternation is what the permitted import
3713
- * spellings produce: an `@src/core` alias resolves to the core source module and
3714
- * prints `.ts`, while a relative import prints the `.js` specifier it was
3715
- * written with. The formatter keeps the call on one line only while the line it
3716
- * prints measures inside the vendored width, and the workspace name is what
3717
- * varies, so the shape is chosen by measuring the candidate: a tab prints as the
3718
- * vendored two columns, and the gate admits a name long enough to push the
3719
- * joined call past 100.
3720
- *
3721
- * @example
3722
- * ```ts
3723
- * import { nameToRewrite } from '@orkestrel/scaffold'
3724
- *
3725
- * nameToRewrite('router').includes("'@orkestrel/router'") // true
3726
- * ```
3727
- */
3728
- function nameToRewrite(name) {
3729
- const specifier = serializeTypeScriptString(`@orkestrel/${name}`);
3730
- const pattern = "/(?:\\.\\.\\/)+core\\/index\\.[jt]s/g";
3731
- const joined = `\t\t\t\t\t\t? content.replaceAll(${pattern}, ${specifier})`;
3732
- if (matchesPrintWidth(joined)) return joined;
3733
- return [
3734
- " ? content.replaceAll(",
3735
- `\t\t\t\t\t\t\t\t${pattern},`,
3736
- `\t\t\t\t\t\t\t\t${specifier},`,
3737
- " )"
3738
- ].join("\n");
3739
- }
3740
- /**
3741
3811
  * Selects the single published environment a package root points at.
3742
3812
  *
3743
3813
  * @param src - The declared published environments.
@@ -3993,9 +4063,11 @@ function matchesDriftReachability(ownership, finding) {
3993
4063
  * @remarks
3994
4064
  * A layer is a deterministic function of the catalog's own edges, so it is
3995
4065
  * computed here rather than stored on a row that could disagree with them.
3996
- * Only RUNTIME edges between catalogued packages count: a development
4066
+ * Only RUNTIME and PEER edges between catalogued packages count: a development
3997
4067
  * dependency reaches no consumer, so it constrains nothing about publish order,
3998
- * and an edge leaving the fleet is a package this catalog does not publish.
4068
+ * and an edge leaving the fleet is a package this catalog does not publish. A
4069
+ * peer edge counts like a runtime edge, because a caret peer range at `0.0.x`
4070
+ * names one exact release the dependent cannot publish ahead of.
3999
4071
  *
4000
4072
  * The order matters because these packages are `0.0.x`, where a caret pins one
4001
4073
  * exact release. Publishing a dependent before its dependency leaves the
@@ -4006,11 +4078,12 @@ function matchesDriftReachability(ownership, finding) {
4006
4078
  * placed in an order that would be wrong. An absent name is the report: compare
4007
4079
  * the returned names against the catalog to find one.
4008
4080
  *
4009
- * @example
4081
+ * @example Fleet catalog
4010
4082
  * ```ts
4011
4083
  * import { catalogToLayers } from '@orkestrel/scaffold'
4012
4084
  *
4013
- * catalogToLayers(entries)[0] // the names that depend on nothing in the fleet
4085
+ * const layers = catalogToLayers(entries)
4086
+ * layers[0] // the names that depend on nothing else in the fleet
4014
4087
  * ```
4015
4088
  */
4016
4089
  function catalogToLayers(entries) {
@@ -4018,7 +4091,7 @@ function catalogToLayers(entries) {
4018
4091
  const pending = /* @__PURE__ */ new Map();
4019
4092
  for (const entry of entries) {
4020
4093
  if (entry.lookup !== "found") continue;
4021
- const edges = entry.dependencies.map((dependency) => dependency.name);
4094
+ const edges = [...entry.dependencies, ...entry.peers].map((dependency) => dependency.name);
4022
4095
  pending.set(entry.name, new Set(edges.filter((name) => published.has(name))));
4023
4096
  }
4024
4097
  const layers = [];
@@ -4232,9 +4305,9 @@ function matchesRange(range, latest) {
4232
4305
  * ```ts
4233
4306
  * import { matchesEngines } from '@orkestrel/scaffold'
4234
4307
  *
4235
- * matchesEngines('>=22.12.0') // true
4308
+ * matchesEngines('>=22.18.0') // true
4236
4309
  * matchesEngines('>=20.0.0') // false
4237
- * matchesEngines('22.12.0') // false
4310
+ * matchesEngines('22.18.0') // false
4238
4311
  * ```
4239
4312
  */
4240
4313
  function matchesEngines(engines) {
@@ -4595,7 +4668,7 @@ function blueprintToScripts(blueprint) {
4595
4668
  scripts["test:policy"] = `${vitest} --project policy`;
4596
4669
  scripts["test:config"] = `${vitest} --project config`;
4597
4670
  if (blueprint.setup) scripts["test:setup"] = `${vitest} --project setup`;
4598
- if (blueprint.guides) scripts["test:guides"] = `${vitest} --project guides`;
4671
+ if (blueprint.guides) scripts["test:guides"] = `node --experimental-strip-types ${GUIDES_TEST_PATH}`;
4599
4672
  if (blueprint.conformance) scripts["test:conformance"] = `${vitest} --project conformance`;
4600
4673
  scripts["test:probe"] = "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe";
4601
4674
  scripts["test:bench"] = "vitest bench --config vite.config.ts --no-cache --project probe";
@@ -4649,13 +4722,16 @@ function blueprintToScripts(blueprint) {
4649
4722
  *
4650
4723
  * @remarks
4651
4724
  * Every direct `test:<project>` script is writable, together with the probe and
4652
- * benchmark workbench scripts. Publishing adds the pack and publication
4653
- * lifecycle scripts. Aggregate test scripts and maintainer-owned gate chains
4654
- * stay outside the region.
4725
+ * benchmark workbench scripts. A workspace carrying guides adds `test:guides`,
4726
+ * whose package-owned entry runs the guides project and handles explicit parity
4727
+ * rewrites. Publishing adds the pack and publication lifecycle scripts.
4728
+ * Aggregate test scripts and
4729
+ * maintainer-owned gate chains stay outside the region.
4655
4730
  *
4656
4731
  * `accepted` carries each generated predecessor the region can replace. The
4657
- * pack hook accepts the build chain emitted before it delegated to `build`. The
4658
- * publication hook accepts the same gate chain without
4732
+ * guides entry accepts the generated command that invoked the guides project
4733
+ * through Vitest alone. The pack hook accepts the build chain emitted before it
4734
+ * delegated to `build`. The publication hook accepts the same gate chain without
4659
4735
  * {@link RELEASE_PROOF_COMMAND}. The value being written is always writable, so
4660
4736
  * it is not repeated there. Any other value is a script the workspace author
4661
4737
  * customized, and {@link replaceManifestScripts} retains it while writing the
@@ -4674,11 +4750,11 @@ function blueprintToWritableScripts(blueprint) {
4674
4750
  const scripts = blueprintToScripts(blueprint);
4675
4751
  const writable = [];
4676
4752
  for (const [name, command] of Object.entries(scripts)) {
4677
- if (!name.startsWith("test:") || name === "test:src" || name === "test:app") continue;
4753
+ if (!(name.startsWith("test:") && name !== "test:src" && name !== "test:app")) continue;
4678
4754
  writable.push({
4679
4755
  name,
4680
4756
  command,
4681
- accepted: []
4757
+ accepted: name === "test:guides" ? ["vitest run --config vite.config.ts --no-cache --reporter=dot --project guides"] : []
4682
4758
  });
4683
4759
  }
4684
4760
  const prepack = scripts.prepack;
@@ -4780,6 +4856,7 @@ function blueprintToManifest(blueprint) {
4780
4856
  devDependencies: blueprintToDevDependencies(blueprint),
4781
4857
  ...Object.keys(peerDependencies).length > 0 ? { peerDependencies } : {},
4782
4858
  ...Object.keys(peerDependenciesMeta).length > 0 ? { peerDependenciesMeta } : {},
4859
+ devEngines: WORKSPACE_DEV_ENGINES,
4783
4860
  engines: { node: blueprint.engines }
4784
4861
  };
4785
4862
  return `${JSON.stringify(manifest, void 0, " ")}\n`;
@@ -4826,6 +4903,21 @@ function blueprintToMachinery(blueprint) {
4826
4903
  * @param blueprint - The workspace specification.
4827
4904
  * @returns Formatter-stable `tsconfig.json` text.
4828
4905
  *
4906
+ * @remarks
4907
+ * `paths` carries the workspace's own published specifiers beside the `@src` and
4908
+ * `@app` aliases, mapped to source. The published set is what
4909
+ * {@link srcToExports} publishes, read the same way here so the two never name
4910
+ * different subpaths, and the `app` axis publishes nothing so it maps nothing.
4911
+ * A package's own name resolves inside its own checkout through that `exports`
4912
+ * map, to the built `dist/` entry, so a module importing it would make the root
4913
+ * typecheck depend on a build; mapping the specifier to source is what keeps
4914
+ * `npm run check` independent of `npm run build`. The root Vite configuration
4915
+ * derives its `alias` record from every `paths` entry, so the same specifiers
4916
+ * resolve to source under Vitest, and every subpath is emitted before the bare
4917
+ * specifier: an alias record is matched in declaration order and a bare pattern
4918
+ * also matches its own subpaths, so the reverse order would resolve
4919
+ * `@orkestrel/<name>/server` through the core entry.
4920
+ *
4829
4921
  * @example
4830
4922
  * ```ts
4831
4923
  * import { blueprintToRootTsconfig, createBlueprint } from '@orkestrel/scaffold'
@@ -4837,9 +4929,21 @@ function blueprintToMachinery(blueprint) {
4837
4929
  */
4838
4930
  function blueprintToRootTsconfig(blueprint) {
4839
4931
  const aliases = [];
4840
- for (const environment of ENVIRONMENTS) if (blueprint.src.includes(environment)) aliases.push(`"@src/${environment}": ["./src/${environment}/index.ts"]`);
4841
- for (const environment of ENVIRONMENTS) if (blueprint.app.includes(environment)) aliases.push(`"@app/${environment}": ["./app/${environment}/index.ts"]`);
4842
- const paths = aliases.map((alias, index) => `\t\t\t${alias}${index === aliases.length - 1 ? "" : ","}`).join("\n");
4932
+ for (const environment of ENVIRONMENTS) if (blueprint.src.includes(environment)) aliases.push([`@src/${environment}`, `./src/${environment}/index.ts`]);
4933
+ for (const environment of ENVIRONMENTS) if (blueprint.app.includes(environment)) aliases.push([`@app/${environment}`, `./app/${environment}/index.ts`]);
4934
+ const root = srcToRoot(blueprint.src);
4935
+ if (root === void 0) for (const environment of ENVIRONMENTS) {
4936
+ if (environment === "core" || !blueprint.src.includes(environment)) continue;
4937
+ const subpath = SRC_MATRIX[environment].path.slice(1);
4938
+ aliases.push([`@orkestrel/${blueprint.name}${subpath}`, `./src/${environment}/index.ts`]);
4939
+ }
4940
+ if (blueprint.src.length > 0) aliases.push([`@orkestrel/${blueprint.name}`, `./src/${root ?? "core"}/index.ts`]);
4941
+ const paths = aliases.map(([specifier, target], index) => {
4942
+ const separator = index === aliases.length - 1 ? "" : ",";
4943
+ const line = `\t\t\t"${specifier}": ["${target}"]${separator}`;
4944
+ if (matchesPrintWidth(line)) return line;
4945
+ return `\t\t\t"${specifier}": [\n\t\t\t\t"${target}"\n\t\t\t]${separator}`;
4946
+ }).join("\n");
4843
4947
  return (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.root.tsconfig, { paths });
4844
4948
  }
4845
4949
  /**
@@ -5072,9 +5176,9 @@ function blueprintToConfigArtifacts(blueprint) {
5072
5176
  for (const environment of blueprint.src) for (const path of SRC_MATRIX[environment].configs) {
5073
5177
  let content = CONFIG_TEMPLATES.vites.src.core;
5074
5178
  if (path === "configs/src/tsconfig.core.json") content = CONFIG_TEMPLATES.tsconfigs.src.core;
5075
- else if (path === "configs/src/vite.browser.config.ts") content = (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.vites.src.browser, { replacement: nameToRewrite(blueprint.name) });
5179
+ else if (path === "configs/src/vite.browser.config.ts") content = CONFIG_TEMPLATES.vites.src.browser;
5076
5180
  else if (path === "configs/src/tsconfig.browser.json") content = CONFIG_TEMPLATES.tsconfigs.src.browser;
5077
- else if (path === "configs/src/vite.server.config.ts") content = (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.vites.src.server, { replacement: nameToRewrite(blueprint.name) });
5181
+ else if (path === "configs/src/vite.server.config.ts") content = CONFIG_TEMPLATES.vites.src.server;
5078
5182
  else if (path === "configs/src/tsconfig.server.json") content = CONFIG_TEMPLATES.tsconfigs.src.server;
5079
5183
  artifacts.push({
5080
5184
  path,
@@ -5517,11 +5621,11 @@ function blueprintToOrchestrationArtifacts(blueprint) {
5517
5621
  }];
5518
5622
  }
5519
5623
  /**
5520
- * Compiles the vendored host artifacts a named workspace plans.
5624
+ * Compiles the vendored host artifacts a workspace plans.
5521
5625
  *
5522
- * @param name - The target workspace's own bare package name.
5523
- * @returns One artifact per vendored path in `HOST_PATHS` order, then the
5524
- * catalog file.
5626
+ * @param blueprint - The workspace specification.
5627
+ * @returns One artifact per selected vendored path in `HOST_PATHS` order, then
5628
+ * the catalog file.
5525
5629
  *
5526
5630
  * @remarks
5527
5631
  * Every artifact is claimed by presence, which is the strongest claim a pure
@@ -5546,15 +5650,16 @@ function blueprintToOrchestrationArtifacts(blueprint) {
5546
5650
  *
5547
5651
  * @example
5548
5652
  * ```ts
5549
- * import { nameToHostArtifacts } from '@orkestrel/scaffold'
5653
+ * import { blueprintToHostArtifacts, createBlueprint } from '@orkestrel/scaffold'
5654
+ *
5655
+ * const blueprint = createBlueprint('router')
5550
5656
  *
5551
- * nameToHostArtifacts('router').some((artifact) => artifact.path === '.claude/settings.json') // true
5552
- * nameToHostArtifacts('router').some((artifact) => artifact.path === '.claude/agents/orkestrel.md') // true
5553
- * nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
5657
+ * blueprintToHostArtifacts(blueprint).some((artifact) => artifact.path === 'tests/guides.test.ts') // false
5658
+ * blueprintToHostArtifacts(blueprint).some((artifact) => artifact.path === 'guides/router.md') // false
5554
5659
  * ```
5555
5660
  */
5556
- function nameToHostArtifacts(name) {
5557
- return [...selectHostPaths(HOST_PATHS, name), CATALOG_AGENT_PATH].map((path) => ({
5661
+ function blueprintToHostArtifacts(blueprint) {
5662
+ return [...selectHostPaths(HOST_PATHS, blueprint.name), CATALOG_AGENT_PATH].map((path) => ({
5558
5663
  path,
5559
5664
  group: inferGroup(path),
5560
5665
  ownership: "presence",
@@ -5597,7 +5702,8 @@ function applyOverrides(artifacts, overrides) {
5597
5702
  });
5598
5703
  }
5599
5704
  /**
5600
- * Replaces declared dependency ranges in package manifest text.
5705
+ * Replaces the runtime and development dependency ranges in package manifest text, and never a
5706
+ * peer range.
5601
5707
  *
5602
5708
  * @param manifest - The manifest text to compile.
5603
5709
  * @param pins - The runtime and development names and replacement ranges.
@@ -6483,12 +6589,18 @@ function overridesToQuestions(overrides, artifacts) {
6483
6589
  * and let the answers disagree, so a blueprint the gate will
6484
6590
  * refuse is still constructible.
6485
6591
  *
6486
- * @example
6592
+ * @example Blueprint
6487
6593
  * ```ts
6488
6594
  * import { createBlueprint } from '@orkestrel/scaffold'
6489
6595
  *
6490
- * createBlueprint('router', { src: ['core'] }).version // '0.0.1'
6491
- * createBlueprint('Router').name // 'Router' — the gate refuses it, this does not
6596
+ * const blueprint = createBlueprint('router', {
6597
+ * src: ['core', 'server'],
6598
+ * dependencies: [{ name: '@orkestrel/emitter', range: '^0.0.5' }],
6599
+ * bin: true,
6600
+ * })
6601
+ *
6602
+ * blueprint.version // '0.0.1'
6603
+ * blueprint.engines // '>=22.18.0'
6492
6604
  * ```
6493
6605
  */
6494
6606
  function createBlueprint(name, input) {
@@ -6552,11 +6664,13 @@ function createBlueprint(name, input) {
6552
6664
  *
6553
6665
  * @example
6554
6666
  * ```ts
6555
- * import { createBlueprint, Compiler } from '@orkestrel/scaffold'
6667
+ * import { Compiler, createBlueprint } from '@orkestrel/scaffold'
6556
6668
  *
6557
6669
  * const compiler = new Compiler()
6558
6670
  * const scaffolding = compiler.compile(createBlueprint('router', { src: ['core'] }))
6559
- * scaffolding.plan?.hash?.length // 16
6671
+ *
6672
+ * scaffolding.plan?.artifacts // every planned file, in group order
6673
+ * scaffolding.stages // one CompileRecord per stage that ran
6560
6674
  * compiler.destroy()
6561
6675
  * ```
6562
6676
  */
@@ -6788,7 +6902,7 @@ var Compiler = class {
6788
6902
  ...blueprintToGuideArtifacts(blueprint),
6789
6903
  ...blueprintToDocumentArtifacts(blueprint),
6790
6904
  ...blueprintToOrchestrationArtifacts(blueprint),
6791
- ...nameToHostArtifacts(blueprint.name)
6905
+ ...blueprintToHostArtifacts(blueprint)
6792
6906
  ];
6793
6907
  }
6794
6908
  #gate(blueprint, draft, artifacts) {
@@ -6869,6 +6983,7 @@ exports.MAX_SCRIPT_LENGTH = MAX_SCRIPT_LENGTH;
6869
6983
  exports.MAX_TOTAL_ARTIFACT_BYTES = MAX_TOTAL_ARTIFACT_BYTES;
6870
6984
  exports.MAX_TOTAL_REGISTRY_BYTES = MAX_TOTAL_REGISTRY_BYTES;
6871
6985
  exports.MINIMUM_NODE_VERSION = MINIMUM_NODE_VERSION;
6986
+ exports.MINIMUM_NPM_VERSION = MINIMUM_NPM_VERSION;
6872
6987
  exports.NAME_PATTERN = NAME_PATTERN;
6873
6988
  exports.ORCHESTRATION_PATH_NAMES = ORCHESTRATION_PATH_NAMES;
6874
6989
  exports.ORCHESTRATION_PATH_PREFIXES = ORCHESTRATION_PATH_PREFIXES;
@@ -6885,6 +7000,7 @@ exports.SRC_MATRIX = SRC_MATRIX;
6885
7000
  exports.ScaffoldError = ScaffoldError;
6886
7001
  exports.TAB_WIDTH = TAB_WIDTH;
6887
7002
  exports.VERSION_PATTERN = VERSION_PATTERN;
7003
+ exports.WORKSPACE_DEV_ENGINES = WORKSPACE_DEV_ENGINES;
6888
7004
  exports.WORKSPACE_OWNED_PATHS = WORKSPACE_OWNED_PATHS;
6889
7005
  exports.applyOverrides = applyOverrides;
6890
7006
  exports.artifactToFinding = artifactToFinding;
@@ -6894,6 +7010,7 @@ exports.blueprintToConfigArtifacts = blueprintToConfigArtifacts;
6894
7010
  exports.blueprintToDevDependencies = blueprintToDevDependencies;
6895
7011
  exports.blueprintToDocumentArtifacts = blueprintToDocumentArtifacts;
6896
7012
  exports.blueprintToGuideArtifacts = blueprintToGuideArtifacts;
7013
+ exports.blueprintToHostArtifacts = blueprintToHostArtifacts;
6897
7014
  exports.blueprintToMachinery = blueprintToMachinery;
6898
7015
  exports.blueprintToManifest = blueprintToManifest;
6899
7016
  exports.blueprintToOrchestrationArtifacts = blueprintToOrchestrationArtifacts;
@@ -6952,8 +7069,6 @@ exports.matchesOrchestrationPath = matchesOrchestrationPath;
6952
7069
  exports.matchesPrintWidth = matchesPrintWidth;
6953
7070
  exports.matchesRange = matchesRange;
6954
7071
  exports.nameToGuide = nameToGuide;
6955
- exports.nameToHostArtifacts = nameToHostArtifacts;
6956
- exports.nameToRewrite = nameToRewrite;
6957
7072
  exports.overridesToQuestions = overridesToQuestions;
6958
7073
  exports.parseBlueprint = parseBlueprint;
6959
7074
  exports.parseCompilerOptions = parseCompilerOptions;