@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.
- package/dist/bin/main.js +144 -23
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +20 -1
- package/dist/host/claude/agents/grok.md +17 -6
- package/dist/host/claude/rules/architecture.md +12 -0
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/tests.md +57 -21
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/dotfiles/oxlintrc.json +1 -0
- package/dist/host/guides/scaffold.md +142 -67
- package/dist/host/tests/config.test.ts +45 -17
- package/dist/host/tests/policy.test.ts +254 -2
- package/dist/host/tests/setupPolicy.ts +297 -34
- package/dist/src/core/index.cjs +176 -179
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +90 -33
- package/dist/src/core/index.d.ts +90 -33
- package/dist/src/core/index.js +171 -180
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +13 -1
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +14 -2
- package/dist/src/server/index.js.map +1 -1
- package/package.json +4 -4
package/dist/src/core/index.js
CHANGED
|
@@ -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
|
|
209
|
+
/** The guide-parity proof whose presence selects the planned `guides` project. */
|
|
200
210
|
var GUIDES_TEST_PATH = "tests/guides.test.ts";
|
|
201
|
-
/** The
|
|
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
|
-
"@
|
|
291
|
-
"@orkestrel/
|
|
292
|
-
"@orkestrel/
|
|
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 {
|
|
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(
|
|
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 {
|
|
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('
|
|
1397
|
-
it('loads
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
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
|
|
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
|
|
3087
|
-
*
|
|
3088
|
-
*
|
|
3089
|
-
*
|
|
3090
|
-
*
|
|
3091
|
-
*
|
|
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
|
|
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.
|
|
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
|
-
...
|
|
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(
|
|
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
|
-
|
|
3475
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
3701
|
-
*
|
|
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
|
|
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.
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
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,
|
|
4168
|
-
* manifest names a core build the workspace never runs
|
|
4169
|
-
* whose required axis is absent, which emits nothing
|
|
4170
|
-
*
|
|
4171
|
-
*
|
|
4172
|
-
*
|
|
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
|
|
4213
|
+
if (blueprint.integration && blueprint.src.length + blueprint.app.length < 2) questions.push({
|
|
4230
4214
|
field: "integration",
|
|
4231
|
-
message: "integration
|
|
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
|