@orkestrel/scaffold 0.0.31 → 0.0.32

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.
@@ -139,6 +139,16 @@ var HOST_PATHS = Object.freeze([
139
139
  "guides/scaffold.md"
140
140
  ]);
141
141
  /**
142
+ * The vendored paths whose present bytes belong to each workspace, frozen.
143
+ *
144
+ * @remarks
145
+ * These paths are copied into a workspace when absent and are never compared
146
+ * or replaced while present. A workspace therefore stops receiving later
147
+ * canonical updates to them. `.gitignore` takes that trade because its correct
148
+ * rules differ by workspace, so scaffold cannot own its bytes.
149
+ */
150
+ var WORKSPACE_OWNED_PATHS = Object.freeze([".gitignore"]);
151
+ /**
142
152
  * The vendored paths a target receives with its executable bit set, frozen.
143
153
  *
144
154
  * @remarks
@@ -196,9 +206,11 @@ var CATALOG_AGENT_PATH = ".claude/agents/orkestrel.md";
196
206
  var SERVICE_SCRIPT_PATH = "scripts/service.sh";
197
207
  /** The shared Vitest global-setup module whose presence makes a workspace `global`. */
198
208
  var GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
199
- /** The guide-parity proof whose physical file selects the fixed `guides` project. */
209
+ /** The guide-parity proof whose presence selects the planned `guides` project. */
200
210
  var GUIDES_TEST_PATH = "tests/guides.test.ts";
201
- /** The installed-package proof whose presence makes a workspace `integration`. */
211
+ /** The packed-package proof whose presence makes a workspace `distribution`. */
212
+ var DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
213
+ /** The cross-environment composition proof whose presence makes a workspace `integration`. */
202
214
  var INTEGRATION_TEST_PATH = "tests/integration.test.ts";
203
215
  /** The official-tooling drift proof whose presence makes a workspace `conformance`. */
204
216
  var CONFORMANCE_TEST_PATH = "tests/conformance.test.ts";
@@ -269,6 +281,10 @@ var MAX_RANGE_LENGTH = 2048;
269
281
  var MAX_PATH_LENGTH = 32767;
270
282
  /** Maximum items accepted in one public collection. */
271
283
  var MAX_COLLECTION_ITEMS = 1e3;
284
+ /** Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. */
285
+ var PRINT_WIDTH = 100;
286
+ /** Columns one tab occupies when the formatter measures a line, matching `tabWidth`. */
287
+ var TAB_WIDTH = 2;
272
288
  /** Maximum findings one audit can produce from a bounded plan and snapshot. */
273
289
  var MAX_AUDIT_FINDINGS = MAX_COLLECTION_ITEMS * 2;
274
290
  /** Maximum bytes accepted for one artifact. */
@@ -287,17 +303,21 @@ var DEFAULT_VERSION = "0.0.1";
287
303
  var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
288
304
  /** The tooling versions scaffold and every generated workspace share. */
289
305
  var BASE_DEV_DEPENDENCIES = Object.freeze({
290
- "@microsoft/api-extractor": "^7.58.12",
291
- "@orkestrel/guide": "^0.0.10",
292
- "@orkestrel/scaffold": "^0.0.31",
306
+ "@orkestrel/guide": "^0.0.11",
307
+ "@orkestrel/scaffold": "^0.0.32",
308
+ "@orkestrel/test": "^0.0.2",
293
309
  "@types/node": "^26.2.0",
294
310
  oxfmt: "^0.62.0",
295
311
  oxlint: "^1.77.0",
296
312
  typescript: "^6.0.3",
297
313
  vite: "~8.2.0",
298
- "vite-plugin-dts": "^5.0.3",
299
314
  vitest: "^4.1.10"
300
315
  });
316
+ /** The development dependencies that emit declarations for published source or an executable. */
317
+ var DECLARATION_DEV_DEPENDENCIES = Object.freeze({
318
+ "@microsoft/api-extractor": "^7.58.12",
319
+ "vite-plugin-dts": "^5.0.3"
320
+ });
301
321
  /** The development dependencies a published browser `src` environment adds. */
302
322
  var SOURCE_BROWSER_DEV_DEPENDENCIES = Object.freeze({
303
323
  "@vitest/browser-playwright": "^4.1.10",
@@ -385,38 +405,12 @@ var CONFIG_TEMPLATES = Object.freeze({
385
405
  vite: `import type { {{viteTypes}} } from 'vite'
386
406
  {{imports}}import { defineConfig, mergeConfig } from 'vitest/config'
387
407
  import tsconfig from './tsconfig.json' with { type: 'json' }
388
- {{helpers}}{{browsers}}import { lstatSync, readdirSync, realpathSync } from 'node:fs'
389
- import { basename, join, parse, relative, resolve as resolvePath, sep } from 'node:path'
390
- import { fileURLToPath, URL } from 'node:url'
408
+ {{helpers}}{{browsers}}import { fileURLToPath, URL } from 'node:url'
391
409
 
392
410
  {{options}}export function resolveWorkspacePath(relativePath: string): string {
393
411
  return fileURLToPath(new URL(relativePath, import.meta.url))
394
412
  }
395
413
 
396
- // A generated root config must classify its own fixed proof without importing
397
- // package source, so the exact-case check stays self-contained over Node APIs.
398
- function isExactCaseFile(path: string): boolean {
399
- const full = resolvePath(path)
400
- try {
401
- const status = lstatSync(full)
402
- if (!status.isFile() || status.isSymbolicLink() || status.nlink !== 1) return false
403
- const root = parse(full).root
404
- const segments = relative(root, full).split(sep)
405
- let parent = root
406
- for (const segment of segments) {
407
- try {
408
- if (!readdirSync(parent).includes(segment)) return false
409
- } catch {
410
- if (basename(realpathSync.native(join(parent, segment))) !== segment) return false
411
- }
412
- parent = join(parent, segment)
413
- }
414
- return true
415
- } catch {
416
- return false
417
- }
418
- }
419
-
420
414
  const resolve = {
421
415
  alias: Object.entries(tsconfig.compilerOptions.paths).reduce((aliases, [key, values]) => {
422
416
  const [path] = values
@@ -552,6 +546,10 @@ const resolve = {
552
546
  setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
553
547
  environment: 'node',
554
548
  browser: { enabled: false },
549
+ // A bin test drives the real executable over a real temporary repository, so it
550
+ // spends seconds in process startup and filesystem work rather than milliseconds.
551
+ // Vitest's five-second default clears one alone and times out under a full suite.
552
+ testTimeout: 15_000,
555
553
  },
556
554
  },
557
555
  options ?? {},
@@ -603,8 +601,7 @@ const resolve = {
603
601
  }
604
602
  }
605
603
 
606
- export function appBrowser(...options: never[]): UserConfig {
607
- if (options.length > 0) throw new Error('Browser configuration overrides are not permitted')
604
+ export function appBrowser(): UserConfig {
608
605
  return applicationBrowser(false)
609
606
  }
610
607
  {{showcaseFactory}}`,
@@ -720,6 +717,23 @@ export const service = (options?: UserConfig): UserConfig =>
720
717
  },
721
718
  options ?? {},
722
719
  )
720
+ `,
721
+ distribution: `export const distribution = (options?: UserConfig): UserConfig =>
722
+ mergeConfig(
723
+ {
724
+ resolve,
725
+ test: {
726
+ name: { label: 'distribution', color: 'cyan' },
727
+ include: ['${DISTRIBUTION_TEST_PATH}'],
728
+ setupFiles: ['./tests/setup.ts'],
729
+ environment: 'node',
730
+ testTimeout: 120_000,
731
+ hookTimeout: 120_000,
732
+ fileParallelism: false,
733
+ },
734
+ },
735
+ options ?? {},
736
+ )
723
737
  `,
724
738
  probe: `// A workbench, not a proof. No gate selects this project.
725
739
  export const probe = (options?: UserConfig): UserConfig =>
@@ -746,9 +760,6 @@ export const probe = (options?: UserConfig): UserConfig =>
746
760
  include: ['${INTEGRATION_TEST_PATH}'],
747
761
  setupFiles: ['./tests/setup.ts'],
748
762
  {{global}} environment: 'node',
749
- testTimeout: 120_000,
750
- hookTimeout: 120_000,
751
- fileParallelism: false,
752
763
  },
753
764
  },
754
765
  options ?? {},
@@ -1376,112 +1387,14 @@ describe('bin entry', () => {
1376
1387
  })
1377
1388
  })
1378
1389
  `,
1379
- integration: `import { spawnSync } from 'node:child_process'
1380
- import { globSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
1381
- import { tmpdir } from 'node:os'
1382
- import { dirname, join, resolve } from 'node:path'
1383
- import { fileURLToPath, pathToFileURL } from 'node:url'
1384
- import { describe, expect, it } from 'vitest'
1385
-
1386
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
1387
- const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
1388
- const registry =
1389
- spawnSync(npm, ['ping', '--fetch-retries=0', '--fetch-timeout=1000', '--loglevel=silent'], {
1390
- cwd: root,
1391
- stdio: 'ignore',
1392
- timeout: 5_000,
1393
- windowsHide: true,
1394
- }).status === 0
1390
+ integration: `{{imports}}import { describe, expect, it } from 'vitest'
1395
1391
 
1396
- describe('installed package consumer', () => {
1397
- it('loads the built package from a process outside the workspace', () => {
1398
- const workspace = mkdtempSync(join(tmpdir(), 'orkestrel-integration-driver-'))
1399
- try {
1400
- const manifest: unknown = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'))
1401
- if (typeof manifest !== 'object' || manifest === null) {
1402
- throw new Error('The package manifest is not a record')
1403
- }
1404
- const module: unknown = Object.getOwnPropertyDescriptor(manifest, 'module')?.value
1405
- if (typeof module !== 'string')
1406
- throw new Error('The package manifest carries no module entry')
1407
- const entry = pathToFileURL(resolve(root, module)).href
1408
- const missing = pathToFileURL(resolve(root, 'dist/outside-integration-control.js')).href
1409
- const control = spawnSync(
1410
- process.execPath,
1411
- ['--input-type=module', '--eval', 'await import(' + JSON.stringify(missing) + ')'],
1412
- { cwd: workspace, encoding: 'utf8', windowsHide: true },
1413
- )
1414
- expect(control.status).not.toBe(0)
1415
- const driven = spawnSync(
1416
- process.execPath,
1417
- ['--input-type=module', '--eval', 'await import(' + JSON.stringify(entry) + ')'],
1418
- { cwd: workspace, encoding: 'utf8', windowsHide: true },
1419
- )
1420
- expect(driven.status).toBe(0)
1421
- expect(driven.stderr).toBe('')
1422
- } finally {
1423
- rmSync(workspace, { recursive: true, force: true })
1424
- }
1392
+ describe('workspace integration', () => {
1393
+ it('loads every selected public barrel together as a composition seed', () => {
1394
+ // Replace this empty-barrel seed with one observable flow that passes one
1395
+ // environment's public result into another.
1396
+ {{actual}}{{expected}})
1425
1397
  })
1426
-
1427
- it.skipIf(!registry)(
1428
- 'installs the packed package into an outside consumer [requires a reachable npm registry]',
1429
- () => {
1430
- const workspace = mkdtempSync(join(tmpdir(), 'orkestrel-integration-install-'))
1431
- const packed = join(workspace, 'packed')
1432
- const consumer = join(workspace, 'consumer')
1433
- try {
1434
- mkdirSync(packed, { recursive: true })
1435
- mkdirSync(consumer, { recursive: true })
1436
- const pack = spawnSync(
1437
- npm,
1438
- ['pack', '--json', '--ignore-scripts', '--pack-destination', packed],
1439
- { cwd: root, encoding: 'utf8', windowsHide: true },
1440
- )
1441
- expect(pack.status).toBe(0)
1442
- const archives = globSync('*.tgz', { cwd: packed })
1443
- expect(archives).toHaveLength(1)
1444
- const archive = archives[0]
1445
- if (archive === undefined) throw new Error('The package archive was not written')
1446
- writeFileSync(
1447
- join(consumer, 'package.json'),
1448
- '{"name":"outside-consumer","private":true,"type":"module"}\\n',
1449
- )
1450
- const install = spawnSync(
1451
- npm,
1452
- ['install', '--ignore-scripts', '--no-audit', '--no-fund', join(packed, archive)],
1453
- { cwd: consumer, encoding: 'utf8', windowsHide: true },
1454
- )
1455
- expect(install.status).toBe(0)
1456
- const manifest: unknown = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'))
1457
- if (typeof manifest !== 'object' || manifest === null) {
1458
- throw new Error('The package manifest is not a record')
1459
- }
1460
- const name: unknown = Object.getOwnPropertyDescriptor(manifest, 'name')?.value
1461
- if (typeof name !== 'string') throw new Error('The package manifest carries no name')
1462
- const control = spawnSync(
1463
- process.execPath,
1464
- [
1465
- '--input-type=module',
1466
- '--eval',
1467
- 'await import(' + JSON.stringify(name + '/outside-integration-control') + ')',
1468
- ],
1469
- { cwd: consumer, encoding: 'utf8', windowsHide: true },
1470
- )
1471
- expect(control.status).not.toBe(0)
1472
- const driven = spawnSync(
1473
- process.execPath,
1474
- ['--input-type=module', '--eval', 'await import(' + JSON.stringify(name) + ')'],
1475
- { cwd: consumer, encoding: 'utf8', windowsHide: true },
1476
- )
1477
- expect(driven.status).toBe(0)
1478
- expect(driven.stderr).toBe('')
1479
- } finally {
1480
- rmSync(workspace, { recursive: true, force: true })
1481
- }
1482
- },
1483
- 300_000,
1484
- )
1485
1398
  })
1486
1399
  `
1487
1400
  }),
@@ -1802,6 +1715,8 @@ var isBlueprint = recordOf({
1802
1715
  engines: isString,
1803
1716
  overrides: andOf(isCollection, arrayOf(isOverride)),
1804
1717
  bin: isBoolean,
1718
+ guides: isBoolean,
1719
+ distribution: isBoolean,
1805
1720
  integration: isBoolean,
1806
1721
  conformance: isBoolean,
1807
1722
  service: isBoolean,
@@ -2397,6 +2312,30 @@ function nameToGuide(name) {
2397
2312
  return `guides/${name.slice(name.lastIndexOf("/") + 1)}.md`;
2398
2313
  }
2399
2314
  /**
2315
+ * Test whether one emitted line fits the vendored formatter width.
2316
+ *
2317
+ * @param line - One emitted line, leading tabs included.
2318
+ * @returns `true` when the expanded line fits.
2319
+ *
2320
+ * @remarks
2321
+ * A generator writes source the formatter then reads back, so a line packed
2322
+ * past the vendored width is rewrapped on the next `format` run and the emitted
2323
+ * bytes stop matching the plan the audit compares against. The generator
2324
+ * therefore measures a candidate line and chooses the shape the formatter would
2325
+ * have chosen. Tabs are expanded first because the formatter counts them as
2326
+ * `TAB_WIDTH` columns rather than as one character.
2327
+ *
2328
+ * @example
2329
+ * ```ts
2330
+ * import { matchesPrintWidth } from '@orkestrel/scaffold'
2331
+ *
2332
+ * matchesPrintWidth('\t\tprojects: [core],') // true
2333
+ * ```
2334
+ */
2335
+ function matchesPrintWidth(line) {
2336
+ return line.replaceAll(" ", " ".repeat(2)).length <= 100;
2337
+ }
2338
+ /**
2400
2339
  * Derive the declaration rewrite a published face's `beforeWriteFile` applies.
2401
2340
  *
2402
2341
  * @param name - The workspace's own bare package name.
@@ -2428,7 +2367,7 @@ function nameToRewrite(name) {
2428
2367
  const specifier = serializeTypeScriptString(`@orkestrel/${name}`);
2429
2368
  const pattern = "/(?:\\.\\.\\/)+core\\/index\\.[jt]s/g";
2430
2369
  const joined = `\t\t\t\t\t\t? content.replaceAll(${pattern}, ${specifier})`;
2431
- if (joined.replaceAll(" ", " ").length <= 100) return joined;
2370
+ if (matchesPrintWidth(joined)) return joined;
2432
2371
  return [
2433
2372
  " ? content.replaceAll(",
2434
2373
  `\t\t\t\t\t\t\t\t${pattern},`,
@@ -3058,6 +2997,7 @@ function srcToExports(src) {
3058
2997
  function blueprintToDevDependencies(blueprint) {
3059
2998
  const merged = {
3060
2999
  ...BASE_DEV_DEPENDENCIES,
3000
+ ...blueprint.src.length > 0 || blueprint.bin ? DECLARATION_DEV_DEPENDENCIES : {},
3061
3001
  ...blueprint.src.includes("browser") ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
3062
3002
  ...blueprint.app.length > 0 ? APP_DEV_DEPENDENCIES : {},
3063
3003
  ...blueprint.app.includes("browser") ? APP_BROWSER_DEV_DEPENDENCIES : {},
@@ -3083,12 +3023,13 @@ function blueprintToDevDependencies(blueprint) {
3083
3023
  * proofs every workspace can pass before it has a public API, and one build per
3084
3024
  * target that actually builds.
3085
3025
  *
3086
- * A proof leaves `test` when a real service or a real install answers it. The
3087
- * installed-package integration proof and the live-service proof therefore run
3088
- * from `prepublishOnly` instead. The conformance proof stays in `test`, because
3089
- * it measures this package against official tooling and drives nothing external:
3090
- * a conformance run may start a server, but it starts its own and reaches it
3091
- * over loopback, so the run stays hermetic.
3026
+ * A publishing workspace isolates distribution and live-service proofs from
3027
+ * `test` and runs them from `prepublishOnly` instead. A private workspace has
3028
+ * no publish lifecycle, so it omits distribution and runs a live-service proof
3029
+ * from `test`. Integration and conformance stay in `test` because they neither
3030
+ * pack nor install the workspace and drive no external service. A conformance
3031
+ * run may start a server, but it starts its own and reaches it over loopback,
3032
+ * so the run stays hermetic.
3092
3033
  *
3093
3034
  * The configuration paths interpolated here are the same ones `SRC_MATRIX` and
3094
3035
  * `APP_MATRIX` list as each environment's configuration files, so a rename in
@@ -3105,7 +3046,8 @@ function blueprintToDevDependencies(blueprint) {
3105
3046
  */
3106
3047
  function blueprintToScripts(blueprint) {
3107
3048
  const publishes = blueprint.src.length > 0;
3108
- const integrates = publishes && blueprint.integration;
3049
+ const distributes = blueprint.distribution && publishes;
3050
+ const integrates = blueprint.integration;
3109
3051
  const compiles = publishes || blueprint.bin;
3110
3052
  const runtime = blueprint.app.filter((environment) => environment !== "core");
3111
3053
  const vitest = "vitest run --config vite.config.ts --no-cache --reporter=dot";
@@ -3136,7 +3078,10 @@ function blueprintToScripts(blueprint) {
3136
3078
  ...blueprint.app.length > 0 ? ["npm run test:app"] : [],
3137
3079
  "npm run test:policy",
3138
3080
  "npm run test:config",
3139
- ...blueprint.conformance ? ["npm run test:conformance"] : []
3081
+ ...blueprint.guides ? ["npm run test:guides"] : [],
3082
+ ...blueprint.conformance ? ["npm run test:conformance"] : [],
3083
+ ...integrates ? ["npm run test:integration"] : [],
3084
+ ...!publishes && blueprint.service ? ["npm run test:service"] : []
3140
3085
  ].join(" && ");
3141
3086
  if (compiles) {
3142
3087
  scripts["test:src"] = [
@@ -3153,8 +3098,10 @@ function blueprintToScripts(blueprint) {
3153
3098
  }
3154
3099
  scripts["test:policy"] = `${vitest} --project policy`;
3155
3100
  scripts["test:config"] = `${vitest} --project config`;
3101
+ if (blueprint.guides) scripts["test:guides"] = `${vitest} --project guides`;
3156
3102
  if (blueprint.conformance) scripts["test:conformance"] = `${vitest} --project conformance`;
3157
3103
  scripts["test:probe"] = "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe";
3104
+ if (distributes) scripts["test:distribution"] = `${vitest} --project distribution`;
3158
3105
  if (integrates) scripts["test:integration"] = `${vitest} --project integration`;
3159
3106
  if (blueprint.service) scripts["test:service"] = `${vitest} --project service`;
3160
3107
  scripts.build = [
@@ -3186,9 +3133,9 @@ function blueprintToScripts(blueprint) {
3186
3133
  scripts.serve = "node dist/app/server/main.cjs";
3187
3134
  scripts["serve:build"] = "npm run build:app:server && npm run serve";
3188
3135
  }
3189
- scripts.prepublishOnly = [
3136
+ if (publishes) scripts.prepublishOnly = [
3190
3137
  "npm run format:check && npm run lint:check && npm run check && npm run build && npm test",
3191
- ...integrates ? ["npm run test:integration"] : [],
3138
+ ...distributes ? ["npm run test:distribution -- --mode release"] : [],
3192
3139
  ...blueprint.service ? ["npm run test:service"] : []
3193
3140
  ].join(" && ");
3194
3141
  return scripts;
@@ -3359,6 +3306,8 @@ function blueprintToRootTsconfig(blueprint) {
3359
3306
  */
3360
3307
  function blueprintToRootVite(blueprint) {
3361
3308
  const machinery = blueprintToMachinery(blueprint);
3309
+ const publishes = blueprint.src.length > 0;
3310
+ const distributes = blueprint.distribution && publishes;
3362
3311
  const imports = [];
3363
3312
  if (machinery.browser) imports.push("import { playwright } from '@vitest/browser-playwright'");
3364
3313
  if (machinery.vue) imports.push("import vue from '@vitejs/plugin-vue'");
@@ -3450,8 +3399,7 @@ function blueprintToRootVite(blueprint) {
3450
3399
  : { assetsInlineLimit: 0 }),
3451
3400
  ` : " assetsInlineLimit: 0,\n";
3452
3401
  const showcaseFactory = machinery.showcase ? `
3453
- export function appShowcase(...options: never[]): UserConfig {
3454
- if (options.length > 0) throw new Error('Showcase configuration overrides are not permitted')
3402
+ export function appShowcase(): UserConfig {
3455
3403
  return applicationBrowser(true)
3456
3404
  }
3457
3405
  ` : "";
@@ -3471,8 +3419,10 @@ export function appShowcase(...options: never[]): UserConfig {
3471
3419
  projects.push("policy");
3472
3420
  factories.push(CONFIG_TEMPLATES.factories.config);
3473
3421
  projects.push("config");
3474
- factories.push(CONFIG_TEMPLATES.factories.guides);
3475
- projects.push(`...(isExactCaseFile(resolveWorkspacePath('${GUIDES_TEST_PATH}')) ? [guides] : [])`);
3422
+ if (blueprint.guides) {
3423
+ factories.push(CONFIG_TEMPLATES.factories.guides);
3424
+ projects.push("guides");
3425
+ }
3476
3426
  if (blueprint.conformance) {
3477
3427
  factories.push(CONFIG_TEMPLATES.factories.conformance);
3478
3428
  projects.push("conformance");
@@ -3481,13 +3431,18 @@ export function appShowcase(...options: never[]): UserConfig {
3481
3431
  factories.push(CONFIG_TEMPLATES.factories.service);
3482
3432
  projects.push("service");
3483
3433
  }
3484
- if (blueprint.src.length > 0 && blueprint.integration) {
3434
+ if (distributes) {
3435
+ factories.push(CONFIG_TEMPLATES.factories.distribution);
3436
+ projects.push("distribution");
3437
+ }
3438
+ if (blueprint.integration) {
3485
3439
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "" }));
3486
3440
  projects.push("integration");
3487
3441
  }
3488
3442
  factories.push(CONFIG_TEMPLATES.factories.probe);
3489
3443
  projects.push("probe");
3490
- const projectRows = `\t\tprojects: [
3444
+ const joinedProjects = `\t\tprojects: [${projects.join(", ")}],`;
3445
+ const projectRows = matchesPrintWidth(joinedProjects) ? joinedProjects : `\t\tprojects: [
3491
3446
  ${projects.map((project) => `\t\t\t${project},`).join("\n")}
3492
3447
  \t],`;
3493
3448
  const body = `${factories.join("\n")}\n`;
@@ -3691,15 +3646,14 @@ function blueprintToSourceArtifacts(blueprint) {
3691
3646
  * Compile every artifact in the `tests` group that is not vendored from the host.
3692
3647
  *
3693
3648
  * @param blueprint - The workspace specification.
3694
- * @returns Shared setup modules, axis tests, and the optional install proof.
3649
+ * @returns Shared setup modules, axis tests, and the optional integration seed.
3695
3650
  *
3696
3651
  * @remarks
3697
3652
  * `tests/setupPolicy.ts`, `tests/policy.test.ts`, and `tests/config.test.ts` are
3698
3653
  * fleet-invariant host artifacts and therefore do not appear here. A guide
3699
3654
  * proof is not emitted while the workspace intentionally has empty barrels and
3700
- * no package guide. The install proof is meaningful only for a published
3701
- * workspace and therefore follows the `src` axis as well as its structural
3702
- * flag.
3655
+ * no package guide. The integration seed follows its structural flag and loads
3656
+ * every selected environment through its public barrel.
3703
3657
  *
3704
3658
  * The conformance proof is not emitted either, for the reason the guide proof
3705
3659
  * is not: it names an official artifact only the package knows, so a generated
@@ -3767,7 +3721,8 @@ function blueprintToTestArtifacts(blueprint) {
3767
3721
  });
3768
3722
  if (blueprint.bin) {
3769
3723
  const specifier = serializeTypeScriptString("../../../src/bin/main.js");
3770
- const statement = specifier.length <= 68 ? `\t\tconst entry = await import(${specifier})` : `\t\tconst entry =\n\t\t\tawait import(${specifier})`;
3724
+ const joined = `\t\tconst entry = await import(${specifier})`;
3725
+ const statement = matchesPrintWidth(joined) ? joined : `\t\tconst entry =\n\t\t\tawait import(${specifier})`;
3771
3726
  artifacts.push({
3772
3727
  path: "tests/src/bin/main.test.ts",
3773
3728
  group: "tests",
@@ -3787,13 +3742,35 @@ function blueprintToTestArtifacts(blueprint) {
3787
3742
  label: serializeTypeScriptString(`app ${environment} entry`)
3788
3743
  })
3789
3744
  });
3790
- if (blueprint.src.length > 0 && blueprint.integration) artifacts.push({
3791
- path: INTEGRATION_TEST_PATH,
3792
- group: "tests",
3793
- ownership: "birth",
3794
- origin: "template",
3795
- content: ARTIFACT_TEMPLATES.tests.integration
3796
- });
3745
+ if (blueprint.integration) {
3746
+ const imports = [];
3747
+ const entries = [];
3748
+ for (const environment of blueprint.src) {
3749
+ const entry = environment === "core" ? "srcCore" : environment === "browser" ? "srcBrowser" : "srcServer";
3750
+ imports.push(`import * as ${entry} from '@src/${environment}'`);
3751
+ entries.push(entry);
3752
+ }
3753
+ for (const environment of blueprint.app) {
3754
+ const entry = environment === "core" ? "appCore" : environment === "browser" ? "appBrowser" : "appServer";
3755
+ imports.push(`import * as ${entry} from '@app/${environment}'`);
3756
+ entries.push(entry);
3757
+ }
3758
+ const joined = `expect([${entries.map((entry) => `Object.keys(${entry})`).join(", ")}]).toStrictEqual(`;
3759
+ const actual = matchesPrintWidth(`\t\t${joined}[`) ? joined : `expect([\n${entries.map((entry) => `\t\t\tObject.keys(${entry}),`).join("\n")}\n\t\t]).toStrictEqual(`;
3760
+ const expectedInline = `[${entries.map(() => "[]").join(", ")}]`;
3761
+ const expected = actual !== joined || matchesPrintWidth(`\t\t${joined}${expectedInline})`) ? expectedInline : `[\n${entries.map(() => " [],").join("\n")}\n\t\t]`;
3762
+ artifacts.push({
3763
+ path: INTEGRATION_TEST_PATH,
3764
+ group: "tests",
3765
+ ownership: "birth",
3766
+ origin: "template",
3767
+ content: fillTemplate(ARTIFACT_TEMPLATES.tests.integration, {
3768
+ imports: `${imports.join("\n")}\n`,
3769
+ actual,
3770
+ expected
3771
+ })
3772
+ });
3773
+ }
3797
3774
  return artifacts;
3798
3775
  }
3799
3776
  /**
@@ -4164,12 +4141,19 @@ function dependenciesToQuestions(dependencies, field, name, range) {
4164
4141
  *
4165
4142
  * A question blocks when it describes a workspace this package cannot generate.
4166
4143
  * Three do not, because each describes a workspace it can describe honestly and
4167
- * should not create: a published axis of several environments without core, whose
4168
- * manifest names a core build the workspace never runs, and a structural flag
4169
- * whose required axis is absent, which emits nothing. Blocking those closed the
4170
- * gate for every verb, and the verbs that read an existing workspace need the
4171
- * plan the gate refused. The caller that chooses the shape refuses the advisory;
4172
- * the callers that read one report it.
4144
+ * should not create: a published axis of several environments without core,
4145
+ * whose manifest names a core build the workspace never runs; a showcase flag
4146
+ * whose required browser axis is absent, which emits nothing; and an
4147
+ * integration flag over fewer than two environments, whose seed does emit and
4148
+ * has nothing to compose across. Blocking any of them closed the gate for every
4149
+ * verb. The verbs that read an existing workspace need the plan the gate
4150
+ * refused. The caller that chooses the shape refuses the advisory; the callers
4151
+ * that read one report it.
4152
+ *
4153
+ * The integration advisory counts both axes together, because the proof it
4154
+ * seeds spans environments rather than published ones: an application of two
4155
+ * environments composes, and a package publishing one does not. Reading either
4156
+ * axis alone withdrew the proof from a workspace that does compose.
4173
4157
  *
4174
4158
  * An environment question carries `ENVIRONMENTS` as its candidates, so a caller
4175
4159
  * reads the accepted values from the question instead of from the documentation.
@@ -4226,9 +4210,9 @@ function blueprintToQuestions(blueprint) {
4226
4210
  candidates: ENVIRONMENTS
4227
4211
  });
4228
4212
  }
4229
- if (blueprint.integration && blueprint.src.length === 0) questions.push({
4213
+ if (blueprint.integration && blueprint.src.length + blueprint.app.length < 2) questions.push({
4230
4214
  field: "integration",
4231
- message: "integration projects a published src, and this workspace declares none, so it emits nothing.",
4215
+ message: "integration drives features across environments, and this workspace declares fewer than two, so its seed composes nothing.",
4232
4216
  blocking: false
4233
4217
  });
4234
4218
  const vendors = /* @__PURE__ */ new Set();
@@ -4245,6 +4229,11 @@ function blueprintToQuestions(blueprint) {
4245
4229
  });
4246
4230
  vendors.add(vendor);
4247
4231
  }
4232
+ if (blueprint.distribution && blueprint.src.length === 0) questions.push({
4233
+ field: "distribution",
4234
+ message: "distribution packs the published source, and this workspace declares none, so it emits nothing.",
4235
+ blocking: false
4236
+ });
4248
4237
  if (blueprint.showcase && !blueprint.app.includes("browser")) questions.push({
4249
4238
  field: "showcase",
4250
4239
  message: "showcase projects a browser app, and this workspace declares none, so it emits nothing.",
@@ -4752,6 +4741,8 @@ function createBlueprint(name, input) {
4752
4741
  engines: input?.engines ?? DEFAULT_ENGINES,
4753
4742
  overrides: input?.overrides ?? [],
4754
4743
  bin: input?.bin ?? false,
4744
+ guides: input?.guides ?? false,
4745
+ distribution: input?.distribution ?? false,
4755
4746
  integration: input?.integration ?? false,
4756
4747
  conformance: input?.conformance ?? false,
4757
4748
  service: input?.service ?? false,
@@ -4782,6 +4773,6 @@ function createCompiler(options) {
4782
4773
  return new Compiler(options);
4783
4774
  }
4784
4775
  //#endregion
4785
- export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, CATALOG_AGENT_PATH, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_NAME_PATTERN, EXTRA_RANGE_PATTERN, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, 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_TOTAL_ARTIFACT_BYTES, MINIMUM_NODE_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, ScaffoldError, VERSION_PATTERN, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToGuideArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSourceArtifacts, blueprintToTestArtifacts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, createCompiler, dependenciesToQuestions, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDependency, isDependencyName, isEnvironment, isFinding, isGroup, isGroups, isHex, isMirror, isOverride, isPath, isPlan, isQuestion, isScaffoldError, isSnapshot, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesRange, nameToGuide, nameToHostArtifacts, nameToRewrite, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
4776
+ export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, CATALOG_AGENT_PATH, 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_NAME_PATTERN, EXTRA_RANGE_PATTERN, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, 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_TOTAL_ARTIFACT_BYTES, MINIMUM_NODE_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, ScaffoldError, TAB_WIDTH, VERSION_PATTERN, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToGuideArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSourceArtifacts, blueprintToTestArtifacts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, createCompiler, dependenciesToQuestions, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDependency, isDependencyName, isEnvironment, isFinding, isGroup, isGroups, isHex, isMirror, isOverride, isPath, isPlan, isQuestion, isScaffoldError, isSnapshot, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, nameToHostArtifacts, nameToRewrite, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
4786
4777
 
4787
4778
  //# sourceMappingURL=index.js.map