@orkestrel/scaffold 0.0.17 → 0.0.19

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 (66) hide show
  1. package/dist/host/AGENTS.md +4 -2
  2. package/dist/host/CLAUDE.md +22 -10
  3. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +267 -0
  4. package/dist/host/agents/skills/enterprise-bootstrap/agents/openai.yaml +4 -0
  5. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +609 -0
  6. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +994 -0
  7. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +59 -0
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +312 -0
  9. package/dist/host/agents/skills/orkestrel-align-packages/references/integration.md +4 -13
  10. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +78 -50
  11. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +81 -0
  12. package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +4 -0
  13. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +75 -0
  14. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +11 -9
  15. package/dist/host/agents/skills/orkestrel-harden-package/references/centralization.md +44 -59
  16. package/dist/host/agents/skills/orkestrel-harden-package/references/hardening.md +14 -6
  17. package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +113 -0
  18. package/dist/host/agents/skills/orkestrel-polish-surface/agents/openai.yaml +4 -0
  19. package/dist/host/agents/skills/orkestrel-polish-surface/references/capture-harness.md +82 -0
  20. package/dist/host/claude/agents/builder.md +2 -0
  21. package/dist/host/claude/agents/codex.md +33 -26
  22. package/dist/host/claude/agents/grok.md +7 -0
  23. package/dist/host/claude/agents/implementer.md +2 -1
  24. package/dist/host/claude/agents/orkestrel.md +20 -15
  25. package/dist/host/claude/agents/planner.md +2 -1
  26. package/dist/host/claude/agents/reviewer.md +6 -0
  27. package/dist/host/claude/rules/documentation.md +1 -0
  28. package/dist/host/claude/rules/names.md +5 -7
  29. package/dist/host/claude/rules/quality.md +7 -5
  30. package/dist/host/claude/rules/styles.md +1 -0
  31. package/dist/host/claude/rules/tests.md +1 -0
  32. package/dist/host/claude/rules/typescript.md +3 -10
  33. package/dist/host/claude/rules/workspace.md +2 -5
  34. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +12 -0
  35. package/dist/host/claude/skills/orkestrel-debrief/SKILL.md +12 -0
  36. package/dist/host/claude/skills/orkestrel-polish-surface/SKILL.md +12 -0
  37. package/dist/host/codex/agents/analyst.toml +6 -3
  38. package/dist/host/codex/agents/builder.toml +3 -2
  39. package/dist/host/codex/agents/checker.toml +4 -2
  40. package/dist/host/codex/agents/grok.toml +3 -1
  41. package/dist/host/codex/agents/implementer.toml +4 -2
  42. package/dist/host/codex/agents/opus.toml +5 -3
  43. package/dist/host/codex/agents/orkestrel.toml +6 -5
  44. package/dist/host/codex/agents/planner.toml +6 -2
  45. package/dist/host/codex/agents/reviewer.toml +7 -2
  46. package/dist/host/codex/config.toml +11 -2
  47. package/dist/host/dotfiles/prettierignore +3 -0
  48. package/dist/host/guides/src/scaffold.md +42 -12
  49. package/dist/host/manifest.json +80 -9
  50. package/dist/host/tests/setupPolicy.ts +4 -3
  51. package/dist/src/core/index.cjs +188 -44
  52. package/dist/src/core/index.cjs.map +1 -1
  53. package/dist/src/core/index.d.cts +17 -6
  54. package/dist/src/core/index.d.ts +17 -6
  55. package/dist/src/core/index.js +188 -45
  56. package/dist/src/core/index.js.map +1 -1
  57. package/dist/src/server/index.cjs +11 -5
  58. package/dist/src/server/index.cjs.map +1 -1
  59. package/dist/src/server/index.d.cts +2 -1
  60. package/dist/src/server/index.d.ts +2 -1
  61. package/dist/src/server/index.js +10 -4
  62. package/dist/src/server/index.js.map +1 -1
  63. package/package.json +4 -4
  64. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +0 -129
  65. package/dist/host/claude/agents/application.md +0 -30
  66. package/dist/host/codex/agents/application.toml +0 -25
@@ -1,5 +1,35 @@
1
1
  {
2
2
  "entries": [
3
+ {
4
+ "storage": "agents/skills/enterprise-bootstrap/SKILL.md",
5
+ "destination": ".agents/skills/enterprise-bootstrap/SKILL.md",
6
+ "executable": false
7
+ },
8
+ {
9
+ "storage": "agents/skills/enterprise-bootstrap/agents/openai.yaml",
10
+ "destination": ".agents/skills/enterprise-bootstrap/agents/openai.yaml",
11
+ "executable": false
12
+ },
13
+ {
14
+ "storage": "agents/skills/enterprise-bootstrap/references/bootstrap-reference.md",
15
+ "destination": ".agents/skills/enterprise-bootstrap/references/bootstrap-reference.md",
16
+ "executable": false
17
+ },
18
+ {
19
+ "storage": "agents/skills/enterprise-bootstrap/references/components.md",
20
+ "destination": ".agents/skills/enterprise-bootstrap/references/components.md",
21
+ "executable": false
22
+ },
23
+ {
24
+ "storage": "agents/skills/enterprise-bootstrap/references/frontend-design.md",
25
+ "destination": ".agents/skills/enterprise-bootstrap/references/frontend-design.md",
26
+ "executable": false
27
+ },
28
+ {
29
+ "storage": "agents/skills/enterprise-bootstrap/references/utilities.md",
30
+ "destination": ".agents/skills/enterprise-bootstrap/references/utilities.md",
31
+ "executable": false
32
+ },
3
33
  {
4
34
  "storage": "agents/skills/orkestrel-align-packages/SKILL.md",
5
35
  "destination": ".agents/skills/orkestrel-align-packages/SKILL.md",
@@ -31,8 +61,18 @@
31
61
  "executable": false
32
62
  },
33
63
  {
34
- "storage": "agents/skills/orkestrel-build-application/references/application.md",
35
- "destination": ".agents/skills/orkestrel-build-application/references/application.md",
64
+ "storage": "agents/skills/orkestrel-debrief/SKILL.md",
65
+ "destination": ".agents/skills/orkestrel-debrief/SKILL.md",
66
+ "executable": false
67
+ },
68
+ {
69
+ "storage": "agents/skills/orkestrel-debrief/agents/openai.yaml",
70
+ "destination": ".agents/skills/orkestrel-debrief/agents/openai.yaml",
71
+ "executable": false
72
+ },
73
+ {
74
+ "storage": "agents/skills/orkestrel-debrief/references/field-testing.md",
75
+ "destination": ".agents/skills/orkestrel-debrief/references/field-testing.md",
36
76
  "executable": false
37
77
  },
38
78
  {
@@ -66,8 +106,18 @@
66
106
  "executable": false
67
107
  },
68
108
  {
69
- "storage": "claude/agents/application.md",
70
- "destination": ".claude/agents/application.md",
109
+ "storage": "agents/skills/orkestrel-polish-surface/SKILL.md",
110
+ "destination": ".agents/skills/orkestrel-polish-surface/SKILL.md",
111
+ "executable": false
112
+ },
113
+ {
114
+ "storage": "agents/skills/orkestrel-polish-surface/agents/openai.yaml",
115
+ "destination": ".agents/skills/orkestrel-polish-surface/agents/openai.yaml",
116
+ "executable": false
117
+ },
118
+ {
119
+ "storage": "agents/skills/orkestrel-polish-surface/references/capture-harness.md",
120
+ "destination": ".agents/skills/orkestrel-polish-surface/references/capture-harness.md",
71
121
  "executable": false
72
122
  },
73
123
  {
@@ -175,6 +225,11 @@
175
225
  "destination": ".claude/settings.json",
176
226
  "executable": false
177
227
  },
228
+ {
229
+ "storage": "claude/skills/enterprise-bootstrap/SKILL.md",
230
+ "destination": ".claude/skills/enterprise-bootstrap/SKILL.md",
231
+ "executable": false
232
+ },
178
233
  {
179
234
  "storage": "claude/skills/orkestrel-align-packages/SKILL.md",
180
235
  "destination": ".claude/skills/orkestrel-align-packages/SKILL.md",
@@ -185,19 +240,24 @@
185
240
  "destination": ".claude/skills/orkestrel-build-application/SKILL.md",
186
241
  "executable": false
187
242
  },
243
+ {
244
+ "storage": "claude/skills/orkestrel-debrief/SKILL.md",
245
+ "destination": ".claude/skills/orkestrel-debrief/SKILL.md",
246
+ "executable": false
247
+ },
188
248
  {
189
249
  "storage": "claude/skills/orkestrel-harden-package/SKILL.md",
190
250
  "destination": ".claude/skills/orkestrel-harden-package/SKILL.md",
191
251
  "executable": false
192
252
  },
193
253
  {
194
- "storage": "codex/agents/analyst.toml",
195
- "destination": ".codex/agents/analyst.toml",
254
+ "storage": "claude/skills/orkestrel-polish-surface/SKILL.md",
255
+ "destination": ".claude/skills/orkestrel-polish-surface/SKILL.md",
196
256
  "executable": false
197
257
  },
198
258
  {
199
- "storage": "codex/agents/application.toml",
200
- "destination": ".codex/agents/application.toml",
259
+ "storage": "codex/agents/analyst.toml",
260
+ "destination": ".codex/agents/analyst.toml",
201
261
  "executable": false
202
262
  },
203
263
  {
@@ -349,22 +409,33 @@
349
409
  "roots": [
350
410
  ".agents",
351
411
  ".agents/skills",
412
+ ".agents/skills/enterprise-bootstrap",
413
+ ".agents/skills/enterprise-bootstrap/agents",
414
+ ".agents/skills/enterprise-bootstrap/references",
352
415
  ".agents/skills/orkestrel-align-packages",
353
416
  ".agents/skills/orkestrel-align-packages/agents",
354
417
  ".agents/skills/orkestrel-align-packages/references",
355
418
  ".agents/skills/orkestrel-build-application",
356
419
  ".agents/skills/orkestrel-build-application/agents",
357
- ".agents/skills/orkestrel-build-application/references",
420
+ ".agents/skills/orkestrel-debrief",
421
+ ".agents/skills/orkestrel-debrief/agents",
422
+ ".agents/skills/orkestrel-debrief/references",
358
423
  ".agents/skills/orkestrel-harden-package",
359
424
  ".agents/skills/orkestrel-harden-package/agents",
360
425
  ".agents/skills/orkestrel-harden-package/references",
426
+ ".agents/skills/orkestrel-polish-surface",
427
+ ".agents/skills/orkestrel-polish-surface/agents",
428
+ ".agents/skills/orkestrel-polish-surface/references",
361
429
  ".claude",
362
430
  ".claude/agents",
363
431
  ".claude/rules",
364
432
  ".claude/skills",
433
+ ".claude/skills/enterprise-bootstrap",
365
434
  ".claude/skills/orkestrel-align-packages",
366
435
  ".claude/skills/orkestrel-build-application",
436
+ ".claude/skills/orkestrel-debrief",
367
437
  ".claude/skills/orkestrel-harden-package",
438
+ ".claude/skills/orkestrel-polish-surface",
368
439
  ".codex",
369
440
  ".codex/agents",
370
441
  ".cursor",
@@ -240,7 +240,7 @@ export function isSelfContained(source: ts.SourceFile): boolean {
240
240
  const clause = statement.importClause
241
241
  const named = clause?.namedBindings
242
242
  const erased =
243
- clause?.isTypeOnly === true ||
243
+ clause?.phaseModifier === ts.SyntaxKind.TypeKeyword ||
244
244
  (clause !== undefined &&
245
245
  clause.name === undefined &&
246
246
  named !== undefined &&
@@ -248,8 +248,9 @@ export function isSelfContained(source: ts.SourceFile): boolean {
248
248
  named.elements.length > 0 &&
249
249
  named.elements.every((element) => element.isTypeOnly))
250
250
  if (erased) continue
251
- if (!ts.isStringLiteral(statement.moduleSpecifier)) return false
252
- const specifier = statement.moduleSpecifier.text
251
+ const moduleSpecifier = statement.moduleSpecifier
252
+ if (!ts.isStringLiteral(moduleSpecifier)) return false
253
+ const specifier = moduleSpecifier.text
253
254
  if (!specifier.startsWith('node:') || !isBuiltin(specifier)) return false
254
255
  builtin = true
255
256
  }
@@ -145,6 +145,8 @@ var HOST_PATHS = Object.freeze([
145
145
  var SERVICE_SCRIPT_PATH = "scripts/service.sh";
146
146
  /** The consumer-owned Vitest global-setup module shared by its independently selected projects. */
147
147
  var GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
148
+ /** The consumer-owned Vite wrapper whose physical presence enables the optional app showcase. */
149
+ var SHOWCASE_CONFIG_PATH = "configs/app/vite.showcase.config.ts";
148
150
  /** The package-name RegExp — lowercase alphanumeric-with-hyphens, letter-first. */
149
151
  var NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
150
152
  /** Maximum bare workspace name length beneath the generated `@orkestrel/` scope. */
@@ -227,7 +229,7 @@ var DEFAULT_VERSION = "0.0.1";
227
229
  /** The `engines.node` range the `blueprint` builder fills. */
228
230
  var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
229
231
  /** The devDependency range generated packages pin `@orkestrel/scaffold` at. */
230
- var SCAFFOLD_RANGE = "^0.0.17";
232
+ var SCAFFOLD_RANGE = "^0.0.19";
231
233
  /** Tooling versions shared by scaffold and every generated workspace. */
232
234
  var BASE_DEV_DEPENDENCIES = Object.freeze({
233
235
  "@microsoft/api-extractor": "^7.58.12",
@@ -427,7 +429,7 @@ function member(name, category, summary, environment = "core") {
427
429
  * `version` / `engines` default `DEFAULT_VERSION` / `DEFAULT_ENGINES`,
428
430
  * `src` defaults `['core']`, and `app` / `keywords` / `dependencies` /
429
431
  * `peers` / `extras` / `overrides` default `[]`, and `bin` / `integration` /
430
- * `service` / `global` default `false`. `description` is OMITTED entirely
432
+ * `service` / `global` / `showcase` default `false`. `description` is OMITTED entirely
431
433
  * when absent, so the result round-trips the exact-record `Blueprint` guard.
432
434
  * @returns A complete `Blueprint`.
433
435
  *
@@ -453,7 +455,8 @@ function blueprint(name, options) {
453
455
  bin: options?.bin ?? false,
454
456
  integration: options?.integration ?? false,
455
457
  service: options?.service ?? false,
456
- global: options?.global ?? false
458
+ global: options?.global ?? false,
459
+ showcase: options?.showcase ?? false
457
460
  };
458
461
  return options?.description === void 0 ? base : {
459
462
  ...base,
@@ -1361,6 +1364,11 @@ function validateBlueprint(spec) {
1361
1364
  text: "At least one src or app environment is required",
1362
1365
  blocking: true
1363
1366
  });
1367
+ if (spec.showcase && !spec.app.includes("browser")) questions.push({
1368
+ field: "showcase",
1369
+ text: "Showcase requires the app browser environment",
1370
+ blocking: true
1371
+ });
1364
1372
  if (spec.src.length > 0) {
1365
1373
  for (const environment of spec.src) if (!ENVIRONMENTS.includes(environment)) questions.push({
1366
1374
  field: "src",
@@ -1824,7 +1832,8 @@ function blueprintShape() {
1824
1832
  bin: (0, _orkestrel_contract.booleanShape)(),
1825
1833
  integration: (0, _orkestrel_contract.booleanShape)(),
1826
1834
  service: (0, _orkestrel_contract.booleanShape)(),
1827
- global: (0, _orkestrel_contract.booleanShape)()
1835
+ global: (0, _orkestrel_contract.booleanShape)(),
1836
+ showcase: (0, _orkestrel_contract.booleanShape)()
1828
1837
  });
1829
1838
  }
1830
1839
  /**
@@ -4467,9 +4476,7 @@ describe('createApplication', () => {
4467
4476
  'x'.repeat(204),
4468
4477
  \`\${' '.repeat(MAX_APPLICATION_NAME_INPUT_LENGTH)}x\`,
4469
4478
  ]) {
4470
- expect(() => Reflect.apply(createApplication, undefined, [value])).toThrowError(
4471
- ApplicationError,
4472
- )
4479
+ expect(() => Reflect.apply(createApplication, undefined, [value])).toThrow(ApplicationError)
4473
4480
  }
4474
4481
  const error = new ApplicationError('CONFIG', 'invalid')
4475
4482
  expect(isApplicationError(error)).toBe(true)
@@ -4544,7 +4551,7 @@ describe('createBrowserApplication', () => {
4544
4551
  { name: 'x'.repeat(204) },
4545
4552
  { name: \`\${' '.repeat(MAX_BROWSER_APPLICATION_NAME_INPUT_LENGTH)}x\` },
4546
4553
  ]) {
4547
- expect(() => Reflect.apply(createBrowserApplication, undefined, [value])).toThrowError(
4554
+ expect(() => Reflect.apply(createBrowserApplication, undefined, [value])).toThrow(
4548
4555
  BrowserApplicationError,
4549
4556
  )
4550
4557
  }
@@ -4569,14 +4576,14 @@ describe('createBrowserApplication', () => {
4569
4576
  const revocable = Proxy.revocable({}, {})
4570
4577
  revocable.revoke()
4571
4578
 
4572
- expect(() => parseBrowserApplicationOptions(accessor)).toThrowError(BrowserApplicationError)
4579
+ expect(() => parseBrowserApplicationOptions(accessor)).toThrow(BrowserApplicationError)
4573
4580
  expect(reads).toBe(0)
4574
- expect(() => Reflect.apply(createBrowserApplication, undefined, [hostile])).toThrowError(
4581
+ expect(() => Reflect.apply(createBrowserApplication, undefined, [hostile])).toThrow(
4582
+ BrowserApplicationError,
4583
+ )
4584
+ expect(() => Reflect.apply(createBrowserApplication, undefined, [revocable.proxy])).toThrow(
4575
4585
  BrowserApplicationError,
4576
4586
  )
4577
- expect(() =>
4578
- Reflect.apply(createBrowserApplication, undefined, [revocable.proxy]),
4579
- ).toThrowError(BrowserApplicationError)
4580
4587
  expect(isBrowserApplicationError(new BrowserApplicationError('CONFIG', 'invalid'))).toBe(true)
4581
4588
  expect(isBrowserApplicationError(new Error('plain'))).toBe(false)
4582
4589
  expect(isBrowserApplicationError(revocable.proxy)).toBe(false)
@@ -4679,20 +4686,20 @@ describe('ApplicationServer', () => {
4679
4686
  })
4680
4687
 
4681
4688
  it('validates direct options before allocating a listener', () => {
4682
- expect(() => createApplicationServer({ host: ' ' })).toThrowError(
4689
+ expect(() => createApplicationServer({ host: ' ' })).toThrow(
4683
4690
  expect.objectContaining({ code: 'CONFIG' }),
4684
4691
  )
4685
- expect(() => createApplicationServer({ port: Number.NaN })).toThrowError(
4692
+ expect(() => createApplicationServer({ port: Number.NaN })).toThrow(
4686
4693
  expect.objectContaining({ code: 'CONFIG' }),
4687
4694
  )
4688
- expect(() => createApplicationServer({ port: -1 })).toThrowError(
4695
+ expect(() => createApplicationServer({ port: -1 })).toThrow(
4689
4696
  expect.objectContaining({ code: 'CONFIG' }),
4690
4697
  )
4691
- expect(() => createApplicationServer({ timeout: 0 })).toThrowError(
4698
+ expect(() => createApplicationServer({ timeout: 0 })).toThrow(
4692
4699
  expect.objectContaining({ code: 'CONFIG' }),
4693
4700
  )
4694
4701
  for (const value of [null, 42, [], { host: 42 }, { port: [42] }]) {
4695
- expect(() => Reflect.apply(createApplicationServer, undefined, [value])).toThrowError(
4702
+ expect(() => Reflect.apply(createApplicationServer, undefined, [value])).toThrow(
4696
4703
  expect.objectContaining({ code: 'CONFIG' }),
4697
4704
  )
4698
4705
  }
@@ -4712,14 +4719,10 @@ describe('ApplicationServer', () => {
4712
4719
  expect(server.port).toBe(0)
4713
4720
 
4714
4721
  process.env.APP_PORT = '1e3'
4715
- expect(() => createApplicationServer()).toThrowError(
4716
- expect.objectContaining({ code: 'CONFIG' }),
4717
- )
4722
+ expect(() => createApplicationServer()).toThrow(expect.objectContaining({ code: 'CONFIG' }))
4718
4723
  process.env.APP_PORT = '0'
4719
4724
  process.env.APP_START_TIMEOUT = '0'
4720
- expect(() => createApplicationServer()).toThrowError(
4721
- expect.objectContaining({ code: 'CONFIG' }),
4722
- )
4725
+ expect(() => createApplicationServer()).toThrow(expect.objectContaining({ code: 'CONFIG' }))
4723
4726
  } finally {
4724
4727
  if (previousHost === undefined) delete process.env.APP_HOST
4725
4728
  else process.env.APP_HOST = previousHost
@@ -5088,15 +5091,13 @@ describe('application environment parsers', () => {
5088
5091
  '999.999.999.999',
5089
5092
  \`\${'a'.repeat(64)}.example\`,
5090
5093
  ])('rejects hostile APP_HOST value %s before listener allocation', (value) => {
5091
- expect(() => parseApplicationHost(value)).toThrowError(
5092
- expect.objectContaining({ code: 'CONFIG' }),
5093
- )
5094
+ expect(() => parseApplicationHost(value)).toThrow(expect.objectContaining({ code: 'CONFIG' }))
5094
5095
  })
5095
5096
 
5096
5097
  it.each([null, 42, [], { port: [42] }, { timeout: 0 }])(
5097
5098
  'rejects hostile option container or leaf value %#',
5098
5099
  (value) => {
5099
- expect(() => parseApplicationServerOptions(value)).toThrowError(
5100
+ expect(() => parseApplicationServerOptions(value)).toThrow(
5100
5101
  expect.objectContaining({ code: 'CONFIG' }),
5101
5102
  )
5102
5103
  },
@@ -5119,7 +5120,7 @@ describe('application environment parsers', () => {
5119
5120
  ])(
5120
5121
  'rejects inherited, unknown, symbolic, accessor, instance, and hostile proxy options %#',
5121
5122
  (value) => {
5122
- expect(() => parseApplicationServerOptions(value)).toThrowError(
5123
+ expect(() => parseApplicationServerOptions(value)).toThrow(
5123
5124
  expect.objectContaining({ code: 'CONFIG' }),
5124
5125
  )
5125
5126
  },
@@ -5569,6 +5570,7 @@ function devDependenciesFor(spec) {
5569
5570
  ...dependencies,
5570
5571
  ...spec.src.includes("browser") ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
5571
5572
  ...spec.app.includes("browser") ? APP_BROWSER_DEV_DEPENDENCIES : {},
5573
+ ...spec.showcase ? { "vite-plugin-singlefile": "^2.3.3" } : {},
5572
5574
  ...spec.bin ? { "@vitest/browser-playwright": SOURCE_BROWSER_DEV_DEPENDENCIES["@vitest/browser-playwright"] } : {}
5573
5575
  };
5574
5576
  }
@@ -5630,7 +5632,7 @@ function packageManifest(spec) {
5630
5632
  }
5631
5633
  if (spec.bin) scripts["test:src:bin"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:bin";
5632
5634
  if (spec.integration) scripts["test:integration"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project integration";
5633
- if (spec.bin && spec.integration) scripts["test:equivalence"] = "node -e \"const c=require('node:child_process'),n=process.platform==='win32'?'npm.cmd':'npm',r=c.spawnSync(n,['run','test:integration'],{stdio:'inherit',env:{...process.env,SCAFFOLD_BOUNDARY_EQUIVALENCE:'1'}});process.exit(r.status??1)\"";
5635
+ if (spec.bin && spec.integration) scripts["test:equivalence"] = "node -e \"const c=require('node:child_process'),p=process.env.npm_execpath;if(p===undefined)process.exit(1);const r=c.spawnSync(process.execPath,[p,'run','test:integration'],{stdio:'inherit',env:{...process.env,SCAFFOLD_BOUNDARY_EQUIVALENCE:'1'}});process.exit(r.status??1)\"";
5634
5636
  if (spec.service) scripts["test:service"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project service";
5635
5637
  if (spec.app.length > 0) {
5636
5638
  scripts["test:app"] = "vitest run --config vite.config.ts --no-cache --reporter=dot " + spec.app.map((environment) => `--project ${APP_MATRIX[environment].project}`).join(" ");
@@ -5652,7 +5654,14 @@ function packageManifest(spec) {
5652
5654
  const runtime = spec.app.filter((environment) => environment !== "core");
5653
5655
  scripts["build:app"] = runtime.length > 0 ? runtime.map((environment) => `npm run build:app:${environment}`).join(" && ") : "npm run check:app:core";
5654
5656
  for (const environment of runtime) scripts[`build:app:${environment}`] = `vite build --config configs/app/vite.${environment}.config.ts`;
5655
- if (spec.app.includes("browser")) scripts.dev = "vite --config configs/app/vite.browser.config.ts";
5657
+ if (spec.app.includes("browser")) {
5658
+ scripts.dev = "vite --config configs/app/vite.browser.config.ts";
5659
+ if (spec.showcase) {
5660
+ scripts.showcase = `vite --config ${SHOWCASE_CONFIG_PATH}`;
5661
+ scripts["build:showcase"] = `vite build --config ${SHOWCASE_CONFIG_PATH}`;
5662
+ scripts.show = "npm run build:showcase && npm run copy dist/showcase/index.html demo/showcase.html";
5663
+ }
5664
+ }
5656
5665
  if (spec.app.includes("server")) {
5657
5666
  scripts.serve = "node dist/app/server/main.cjs";
5658
5667
  scripts["serve:build"] = "npm run build:app:server && npm run serve";
@@ -5724,6 +5733,7 @@ function rootTsconfig(src, app = []) {
5724
5733
  target: "ESNext",
5725
5734
  module: "ESNext",
5726
5735
  moduleResolution: "bundler",
5736
+ allowImportingTsExtensions: true,
5727
5737
  lib: [
5728
5738
  "ESNext",
5729
5739
  "DOM",
@@ -5769,20 +5779,22 @@ function rootTsconfig(src, app = []) {
5769
5779
  * @param src - The declared published `Environment[]`.
5770
5780
  * @param app - The declared application `Environment[]`, defaulting to none.
5771
5781
  * @param bin - Whether the workspace also builds its own executable.
5782
+ * @param showcase - Whether the workspace carries its optional app showcase.
5772
5783
  * @returns The machinery set the generated header renders.
5773
5784
  *
5774
5785
  * @example
5775
5786
  * ```ts
5776
- * viteMachinery(['core']) // { browser: false, vue: false, output: true }
5777
- * viteMachinery([], ['core']) // { browser: false, vue: false, output: false }
5787
+ * viteMachinery(['core']) // { browser: false, vue: false, output: true, showcase: false }
5788
+ * viteMachinery([], ['core']) // { browser: false, vue: false, output: false, showcase: false }
5778
5789
  * ```
5779
5790
  */
5780
- function viteMachinery(src, app = [], bin = false) {
5791
+ function viteMachinery(src, app = [], bin = false, showcase = false) {
5781
5792
  const unbuilt = src.length === 0 && app.length > 0 && !bin && app.every((environment) => environment === "core");
5782
5793
  return {
5783
5794
  browser: src.includes("browser") || app.includes("browser"),
5784
5795
  vue: app.includes("browser"),
5785
- output: !unbuilt
5796
+ output: !unbuilt,
5797
+ showcase: showcase && app.includes("browser")
5786
5798
  };
5787
5799
  }
5788
5800
  /**
@@ -5896,7 +5908,7 @@ ${renderedRegistrations.map((registration) => ` ${registration},`).join("\n")}
5896
5908
  * ```
5897
5909
  */
5898
5910
  function viteHeader(machinery) {
5899
- const { browser: needsBrowser, vue: needsVue, output: needsOutput } = machinery;
5911
+ const { browser: needsBrowser, vue: needsVue, output: needsOutput, showcase: needsShowcase } = machinery;
5900
5912
  const playwrightTypeImports = needsBrowser ? `import type { PlaywrightProviderOptions } from '@vitest/browser-playwright'
5901
5913
  ` : "";
5902
5914
  const playwrightImports = needsBrowser ? `import { playwright } from '@vitest/browser-playwright'
@@ -5904,6 +5916,8 @@ import { chromium } from 'playwright'
5904
5916
  ` : "";
5905
5917
  const vueImports = needsVue ? `import vue from '@vitejs/plugin-vue'
5906
5918
  import { parse as parseVue } from 'vue/compiler-sfc'
5919
+ ` : "";
5920
+ const showcaseImports = needsShowcase ? `import { viteSingleFile } from 'vite-plugin-singlefile'
5907
5921
  ` : "";
5908
5922
  const viteTypeImports = needsVue ? `import type {
5909
5923
  CSSOptions,
@@ -6963,6 +6977,31 @@ ${EXPORT_KEYWORD} function finalizeHtml(): Plugin {
6963
6977
  }
6964
6978
  }
6965
6979
 
6980
+ ` : ""}${needsShowcase ? `${EXPORT_KEYWORD} ${CONST_KEYWORD} SHOWCASE_SECURITY_POLICY =
6981
+ "base-uri 'none'; object-src 'none'; script-src 'self' 'unsafe-inline'; script-src-attr 'none'; style-src 'self' 'unsafe-inline'"
6982
+
6983
+ ${EXPORT_KEYWORD} function showcaseHtml(): Plugin {
6984
+ return {
6985
+ name: 'orkestrel-showcase-html',
6986
+ enforce: 'post',
6987
+ transformIndexHtml: {
6988
+ order: 'post',
6989
+ handler(html, context) {
6990
+ if (!isBrowserHtmlEntry(context.filename)) return undefined
6991
+ if (!html.includes(HTML_SECURITY_META)) {
6992
+ throw new Error('[orkestrel-showcase] Browser HTML must retain its security policy')
6993
+ }
6994
+ const stamp = new Date().toISOString()
6995
+ return html.replace(
6996
+ HTML_SECURITY_META,
6997
+ HTML_SECURITY_META.replace(HTML_SECURITY_POLICY, SHOWCASE_SECURITY_POLICY) +
6998
+ \`\\n\\t\\t<meta name="build-id" content="\${stamp}" />\`,
6999
+ )
7000
+ },
7001
+ },
7002
+ }
7003
+ }
7004
+
6966
7005
  ` : ""}${EXPORT_KEYWORD} async function environmentAssetSources(
6967
7006
  code: string,
6968
7007
  id: string,
@@ -7274,7 +7313,7 @@ ${needsBrowser ? `import {
7274
7313
  resolve as resolvePath,
7275
7314
  sep,
7276
7315
  } from 'node:path'` : `import { dirname, isAbsolute, relative, resolve as resolvePath, sep } from 'node:path'`}
7277
- ${playwrightImports}${vueImports}${needsBrowser ? `\n/** Chromium executable layouts inside a \`chromium-<revision>\` browsers-directory entry, per platform. */
7316
+ ${playwrightImports}${vueImports}${showcaseImports}${needsBrowser ? `\n/** Chromium executable layouts inside a \`chromium-<revision>\` browsers-directory entry, per platform. */
7278
7317
  ${EXPORT_KEYWORD} ${CONST_KEYWORD} CHROMIUM_LAYOUTS = Object.freeze([
7279
7318
  'chrome-linux/chrome',
7280
7319
  'chrome-linux64/chrome',
@@ -7824,6 +7863,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
7824
7863
  outDir: 'dist/src/server',
7825
7864
  target: 'node22',
7826
7865
  rolldownOptions: {
7866
+ platform: 'node',
7827
7867
  external: (id: string) => id.startsWith('node:') || id.startsWith('@orkestrel/'),
7828
7868
  },
7829
7869
  },
@@ -7947,6 +7987,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
7947
7987
  outDir: 'dist/src/server',
7948
7988
  target: 'node22',
7949
7989
  rolldownOptions: {
7990
+ platform: 'node',
7950
7991
  external: (id: string) =>
7951
7992
  id === '@src/core' || id.startsWith('node:') || id.startsWith('@orkestrel/'),
7952
7993
  output: [
@@ -8021,7 +8062,7 @@ ${renderedTest}
8021
8062
  */
8022
8063
  function applicationViteConfig(src, app, facts = {}) {
8023
8064
  const hasSourceCore = src.includes("core");
8024
- const machinery = viteMachinery(src, app, facts.bin === true);
8065
+ const machinery = viteMachinery(src, app, facts.bin === true, facts.showcase === true);
8025
8066
  const header = viteHeader(machinery);
8026
8067
  const blocks = [];
8027
8068
  if (src.includes("core")) blocks.push(`
@@ -8131,6 +8172,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
8131
8172
  outDir: 'dist/src/server',
8132
8173
  target: 'node22',
8133
8174
  rolldownOptions: {
8175
+ platform: 'node',
8134
8176
  external: (id: string) =>
8135
8177
  ${coreExternal}id.startsWith('node:') || id.startsWith('@orkestrel/'),${coreOutput}
8136
8178
  },
@@ -8165,8 +8207,98 @@ ${EXPORT_KEYWORD} const appCore = (config?: UserConfig): UserConfig =>
8165
8207
  config ?? {},
8166
8208
  )
8167
8209
  `);
8168
- if (app.includes("browser")) blocks.push(`
8169
- ${EXPORT_KEYWORD} function appBrowser(...config: readonly never[]): UserConfig {
8210
+ if (app.includes("browser")) blocks.push(facts.showcase === true ? `
8211
+ ${FUNCTION_KEYWORD} applicationBrowser(showcase: boolean): UserConfig {
8212
+ const output = showcase ? 'dist/showcase' : 'dist/app/browser'
8213
+ return {
8214
+ resolve,
8215
+ css: ENVIRONMENT_CSS,
8216
+ html: environmentHtml(),
8217
+ plugins: [
8218
+ restoreHtml(),
8219
+ outputBoundary(output),
8220
+ environmentBoundary('app/browser'),
8221
+ vue(),
8222
+ prepareHtml(),
8223
+ finalizeHtml(),
8224
+ ...(showcase
8225
+ ? [
8226
+ viteSingleFile({
8227
+ removeViteModuleLoader: true,
8228
+ useRecommendedBuildConfig: true,
8229
+ }),
8230
+ showcaseHtml(),
8231
+ ]
8232
+ : []),
8233
+ ],
8234
+ root: resolveWorkspacePath('app/browser'),
8235
+ publicDir: false,
8236
+ server: {
8237
+ fs: {
8238
+ strict: true,
8239
+ allow: [...browserServerRoots()],
8240
+ },
8241
+ },
8242
+ build: {
8243
+ ...(showcase
8244
+ ? {
8245
+ cssMinify: 'lightningcss',
8246
+ minify: 'oxc',
8247
+ modulePreload: false,
8248
+ reportCompressedSize: false,
8249
+ sourcemap: false,
8250
+ target: 'esnext',
8251
+ }
8252
+ : { assetsInlineLimit: 0 }),
8253
+ emptyOutDir: true,
8254
+ outDir: resolveWorkspacePath(output),
8255
+ rolldownOptions: {
8256
+ input: resolveWorkspacePath('${APP_MATRIX.browser.entry}'),
8257
+ },
8258
+ },
8259
+ test: {
8260
+ name: { label: '${APP_MATRIX.browser.project}', color: 'blue' },
8261
+ root: resolveWorkspacePath('.'),
8262
+ dir: resolveWorkspacePath('.'),
8263
+ include: ['tests/app/browser/**/*.test.ts'],
8264
+ setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
8265
+ deps: {
8266
+ optimizer: {
8267
+ client: {
8268
+ enabled: true,
8269
+ include: ['vue', ...BROWSER_TEST_DEPENDENCIES],
8270
+ },
8271
+ },
8272
+ },
8273
+ browser: {
8274
+ enabled: true,
8275
+ provider: playwright(browserOptions),
8276
+ instances: [{ browser: 'chromium', headless: true }],
8277
+ },
8278
+ fileParallelism: false,
8279
+ },
8280
+ }
8281
+ }
8282
+
8283
+ ${EXPORT_KEYWORD} function appBrowser(...config: never[]): UserConfig {
8284
+ if (config.length > 0) {
8285
+ throw new Error(
8286
+ '[orkestrel-environment-boundary] Browser configuration overrides are not permitted by the generated boundary',
8287
+ )
8288
+ }
8289
+ return applicationBrowser(false)
8290
+ }
8291
+
8292
+ ${EXPORT_KEYWORD} function appShowcase(...config: never[]): UserConfig {
8293
+ if (config.length > 0) {
8294
+ throw new Error(
8295
+ '[orkestrel-environment-boundary] Showcase configuration overrides are not permitted by the generated boundary',
8296
+ )
8297
+ }
8298
+ return applicationBrowser(true)
8299
+ }
8300
+ ` : `
8301
+ ${EXPORT_KEYWORD} function appBrowser(...config: never[]): UserConfig {
8170
8302
  if (config.length > 0) {
8171
8303
  throw new Error(
8172
8304
  '[orkestrel-environment-boundary] Browser configuration overrides are not permitted by the generated boundary',
@@ -8309,7 +8441,7 @@ import {
8309
8441
  outputBoundary,
8310
8442
  srcCore,
8311
8443
  resolveWorkspacePath,
8312
- } from '../../vite.config'
8444
+ } from '../../vite.config.ts'
8313
8445
 
8314
8446
  export default defineConfig(
8315
8447
  srcCore({
@@ -8394,7 +8526,7 @@ function srcViteConfig(environment) {
8394
8526
  const anchor = environment === "browser" ? "srcBrowser" : "srcServer";
8395
8527
  return `import { defineConfig } from 'vite'
8396
8528
  import dts from 'vite-plugin-dts'
8397
- import { ${anchor}, resolveWorkspacePath } from '../../vite.config'
8529
+ import { ${anchor}, resolveWorkspacePath } from '../../vite.config.ts'
8398
8530
 
8399
8531
  // Types are bundled inline by vite-plugin-dts (see configs/src/vite.core.config.ts
8400
8532
  // for the same pattern).
@@ -8447,7 +8579,7 @@ function binTsconfig() {
8447
8579
  */
8448
8580
  function binViteConfig() {
8449
8581
  return `import { defineConfig } from 'vite'
8450
- import { srcBin } from '../../vite.config'
8582
+ import { srcBin } from '../../vite.config.ts'
8451
8583
 
8452
8584
  // The \`scaffold\` executable build — a single ESM lib file, no declarations (an
8453
8585
  // executable ships no types), with the \`#!/usr/bin/env node\` shebang re-emitted via
@@ -8514,7 +8646,7 @@ function appTsconfig(environment, hasCore) {
8514
8646
  function appViteConfig(environment) {
8515
8647
  const anchor = environment === "browser" ? "appBrowser" : "appServer";
8516
8648
  return `import { defineConfig } from 'vite'
8517
- import { ${anchor} } from '../../vite.config'
8649
+ import { ${anchor} } from '../../vite.config.ts'
8518
8650
 
8519
8651
  export default defineConfig(${anchor}())
8520
8652
  `;
@@ -8646,6 +8778,17 @@ function configArtifacts(spec) {
8646
8778
  });
8647
8779
  }
8648
8780
  }
8781
+ if (spec.showcase) artifacts.push({
8782
+ path: SHOWCASE_CONFIG_PATH,
8783
+ group: "configs",
8784
+ origin: "computed",
8785
+ environment: "browser",
8786
+ content: `import { defineConfig } from 'vite'
8787
+ import { appShowcase } from '../../vite.config.ts'
8788
+
8789
+ export default defineConfig(appShowcase())
8790
+ `
8791
+ });
8649
8792
  return artifacts;
8650
8793
  }
8651
8794
  /**
@@ -9829,6 +9972,7 @@ exports.PlanManager = PlanManager;
9829
9972
  exports.SCAFFOLD_RANGE = SCAFFOLD_RANGE;
9830
9973
  exports.SERVICE_SCRIPT_PATH = SERVICE_SCRIPT_PATH;
9831
9974
  exports.SETUP_NODE_ACTION_SHA = SETUP_NODE_ACTION_SHA;
9975
+ exports.SHOWCASE_CONFIG_PATH = SHOWCASE_CONFIG_PATH;
9832
9976
  exports.SOURCE_BROWSER_DEV_DEPENDENCIES = SOURCE_BROWSER_DEV_DEPENDENCIES;
9833
9977
  exports.SRC_MATRIX = SRC_MATRIX;
9834
9978
  exports.SYNC_BASELINE_PATTERN = SYNC_BASELINE_PATTERN;