@orkestrel/scaffold 0.0.25 → 0.0.26
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/README.md +5 -0
- package/dist/bin/main.js +45 -4
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +40 -11
- package/dist/host/claude/agents/orkestrel.md +63 -48
- package/dist/host/claude/rules/documentation.md +1 -1
- package/dist/host/guides/guide.md +211 -100
- package/dist/host/guides/scaffold.md +165 -10
- package/dist/host/manifest.json +5 -5
- package/dist/host/tests/config.test.ts +30 -7
- package/dist/src/core/index.cjs +242 -149
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +185 -98
- package/dist/src/core/index.d.ts +185 -98
- package/dist/src/core/index.js +240 -150
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +171 -66
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +140 -26
- package/dist/src/server/index.d.ts +140 -26
- package/dist/src/server/index.js +173 -69
- package/dist/src/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/src/core/index.cjs
CHANGED
|
@@ -140,6 +140,25 @@ var HOST_PATHS = Object.freeze([
|
|
|
140
140
|
"guides/scaffold.md"
|
|
141
141
|
]);
|
|
142
142
|
/**
|
|
143
|
+
* The vendored paths a target receives with its executable bit set, frozen.
|
|
144
|
+
*
|
|
145
|
+
* @remarks
|
|
146
|
+
* Declared rather than read from the staging host's filesystem, because that
|
|
147
|
+
* reading is not portable: Windows carries no executable bit, so a host staged
|
|
148
|
+
* there reports every file non-executable and every target receives hooks it
|
|
149
|
+
* cannot run. Declaring the set here makes one checkout stage one manifest on
|
|
150
|
+
* every host.
|
|
151
|
+
*
|
|
152
|
+
* Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
|
|
153
|
+
* that must run when a target invokes it belongs here the moment it is vendored.
|
|
154
|
+
*/
|
|
155
|
+
var EXECUTABLE_PATHS = Object.freeze([
|
|
156
|
+
"scripts/codex.sh",
|
|
157
|
+
"scripts/cursor.sh",
|
|
158
|
+
"scripts/deps.sh",
|
|
159
|
+
"scripts/ollama.sh"
|
|
160
|
+
]);
|
|
161
|
+
/**
|
|
143
162
|
* The path prefixes whose contents instruct or wire an agent, frozen.
|
|
144
163
|
*
|
|
145
164
|
* @remarks
|
|
@@ -227,7 +246,7 @@ var ENGINES_PATTERN = /^>=(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
|
|
227
246
|
var HEX_PATTERN = /^(?:[0-9a-f]{2})*$/;
|
|
228
247
|
/** Unicode controls, formatting controls, and line and paragraph separators rejected in text. */
|
|
229
248
|
var CONTROL_CHARACTER_PATTERN = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
|
|
230
|
-
/** Visible characters a
|
|
249
|
+
/** Visible characters a target-relative path and a Markdown path cell both forbid. */
|
|
231
250
|
var INVALID_PATH_CHARACTER_PATTERN = /[<>:"|?*\\]/;
|
|
232
251
|
/**
|
|
233
252
|
* Maximum bare workspace name length.
|
|
@@ -245,6 +264,8 @@ var MAX_RANGE_LENGTH = 2048;
|
|
|
245
264
|
var MAX_PATH_LENGTH = 32767;
|
|
246
265
|
/** Maximum items accepted in one public collection. */
|
|
247
266
|
var MAX_COLLECTION_ITEMS = 1e3;
|
|
267
|
+
/** Maximum findings one audit can produce from a bounded plan and snapshot. */
|
|
268
|
+
var MAX_AUDIT_FINDINGS = MAX_COLLECTION_ITEMS * 2;
|
|
248
269
|
/** Maximum bytes accepted for one artifact. */
|
|
249
270
|
var MAX_ARTIFACT_BYTES = 5242880;
|
|
250
271
|
/** Maximum length of the hexadecimal string carrying one artifact's bytes. */
|
|
@@ -263,7 +284,7 @@ var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
|
|
|
263
284
|
var BASE_DEV_DEPENDENCIES = Object.freeze({
|
|
264
285
|
"@microsoft/api-extractor": "^7.58.12",
|
|
265
286
|
"@orkestrel/guide": "^0.0.9",
|
|
266
|
-
"@orkestrel/scaffold": "^0.0.
|
|
287
|
+
"@orkestrel/scaffold": "^0.0.26",
|
|
267
288
|
"@types/node": "^26.2.0",
|
|
268
289
|
oxfmt: "^0.62.0",
|
|
269
290
|
oxlint: "^1.77.0",
|
|
@@ -356,12 +377,10 @@ var CONFIG_TEMPLATES = Object.freeze({
|
|
|
356
377
|
"exclude": ["node_modules", "dist", "tmp"]
|
|
357
378
|
}
|
|
358
379
|
`,
|
|
359
|
-
vite: `import type {
|
|
360
|
-
{{imports}}
|
|
361
|
-
import { defineConfig, mergeConfig } from 'vitest/config'
|
|
380
|
+
vite: `import type { {{viteTypes}} } from 'vite'
|
|
381
|
+
{{imports}}import { defineConfig, mergeConfig } from 'vitest/config'
|
|
362
382
|
import tsconfig from './tsconfig.json' with { type: 'json' }
|
|
363
|
-
import {
|
|
364
|
-
import { lstatSync, readdirSync, realpathSync } from 'node:fs'
|
|
383
|
+
{{helpers}}import { lstatSync, readdirSync, realpathSync } from 'node:fs'
|
|
365
384
|
import { basename, join, parse, relative, resolve as resolvePath, sep } from 'node:path'
|
|
366
385
|
import { fileURLToPath, URL } from 'node:url'
|
|
367
386
|
|
|
@@ -553,14 +572,9 @@ const resolve = {
|
|
|
553
572
|
`,
|
|
554
573
|
browser: `function applicationBrowser(showcase: boolean): UserConfig {
|
|
555
574
|
const output = showcase ? 'dist/showcase' : 'dist/app/browser'
|
|
556
|
-
return {
|
|
575
|
+
{{showcasePlugins}} return {
|
|
557
576
|
resolve,
|
|
558
|
-
|
|
559
|
-
outputBoundary(output),
|
|
560
|
-
environmentBoundary('app/browser'),
|
|
561
|
-
vue(),
|
|
562
|
-
{{showcasePlugin}} ],
|
|
563
|
-
root: resolveWorkspacePath('app/browser'),
|
|
577
|
+
{{plugins}} root: resolveWorkspacePath('app/browser'),
|
|
564
578
|
publicDir: false,
|
|
565
579
|
build: {
|
|
566
580
|
{{showcaseBuild}} emptyOutDir: true,
|
|
@@ -943,9 +957,13 @@ export default defineConfig(appShowcase())
|
|
|
943
957
|
*
|
|
944
958
|
* @remarks
|
|
945
959
|
* Builders in `compilers.ts` fill every varying span through
|
|
946
|
-
* `@orkestrel/template`. Empty barrels and setup modules are
|
|
947
|
-
* generated workspace starts with no sample domain API, while
|
|
948
|
-
* Vitest project gets a real test that proves its barrel has no
|
|
960
|
+
* `@orkestrel/template`. Empty barrels, entries, and setup modules are
|
|
961
|
+
* intentional: the generated workspace starts with no sample domain API, while
|
|
962
|
+
* each selected Vitest project gets a real test that proves its barrel has no
|
|
963
|
+
* starter exports. An entry starts empty for the same reason its barrel does,
|
|
964
|
+
* and because the vendored lint config refuses an unassigned import outside a
|
|
965
|
+
* stylesheet, so a starter `import './index.js'` would fail the workspace's own
|
|
966
|
+
* `lint:check` on the day it is written.
|
|
949
967
|
*
|
|
950
968
|
* @example
|
|
951
969
|
* ```ts
|
|
@@ -957,8 +975,6 @@ export default defineConfig(appShowcase())
|
|
|
957
975
|
var ARTIFACT_TEMPLATES = Object.freeze({
|
|
958
976
|
source: Object.freeze({
|
|
959
977
|
empty: "",
|
|
960
|
-
main: `import './index.js'
|
|
961
|
-
`,
|
|
962
978
|
browser: `<!doctype html>
|
|
963
979
|
<html lang="en">
|
|
964
980
|
<head>
|
|
@@ -1143,16 +1159,20 @@ printf '%s\\n' \\
|
|
|
1143
1159
|
* The one error this package throws, carrying the coded reason it was raised.
|
|
1144
1160
|
*
|
|
1145
1161
|
* @remarks
|
|
1146
|
-
*
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
* `
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1162
|
+
* Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
|
|
1163
|
+
* any call made after teardown, `TARGET` for a destination that is not what the
|
|
1164
|
+
* caller's observation said it was, `WRITE` for a mutation that could not be
|
|
1165
|
+
* completed, `FETCH` for an upstream read that produced no answer the caller can
|
|
1166
|
+
* be given, and `BLOCKED` for a refused blueprint.
|
|
1167
|
+
*
|
|
1168
|
+
* `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
|
|
1169
|
+
* — this blueprint will not be built — and the questions say which. The compiler
|
|
1170
|
+
* answers its refusal rather than throwing it: the gate fails closed, returns the
|
|
1171
|
+
* questions that closed it, and records `BLOCKED` on its stage, so a caller reads
|
|
1172
|
+
* that refusal from the value it asked for. A verb that creates a workspace
|
|
1173
|
+
* throws it, because it chose the shape and has nothing to hand back. A blocking
|
|
1174
|
+
* question closed the gate; a non-blocking one is a shape this package can
|
|
1175
|
+
* describe and declines to create.
|
|
1156
1176
|
*
|
|
1157
1177
|
* `context` carries whatever the raising site can say about the failure. It is
|
|
1158
1178
|
* `unknown` because nothing narrows it usefully at the catch site; read it for
|
|
@@ -1206,19 +1226,20 @@ function isScaffoldError(value) {
|
|
|
1206
1226
|
//#endregion
|
|
1207
1227
|
//#region src/core/validators.ts
|
|
1208
1228
|
/**
|
|
1209
|
-
* Narrow a value to a
|
|
1229
|
+
* Narrow a value to a logical target-relative path.
|
|
1210
1230
|
*
|
|
1211
1231
|
* @param value - The candidate path.
|
|
1212
1232
|
* @returns `true` for a bounded relative path with no traversal, empty segment,
|
|
1213
|
-
* control character, or
|
|
1233
|
+
* control character, or reserved syntax character.
|
|
1214
1234
|
*
|
|
1215
1235
|
* @remarks
|
|
1216
1236
|
* Every path this package reads or writes passes here, so one law covers a
|
|
1217
1237
|
* planned artifact, an override target, an audit finding, a guide mirror, and a
|
|
1218
1238
|
* snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
|
|
1219
1239
|
* what stops a caller-supplied path from naming a destination outside the
|
|
1220
|
-
* target
|
|
1221
|
-
*
|
|
1240
|
+
* target. Host-location validation is a separate server boundary: this guard
|
|
1241
|
+
* does not reject a device spelling, a trailing dot or space, or a segment that
|
|
1242
|
+
* exceeds a host filesystem's byte ceiling.
|
|
1222
1243
|
*
|
|
1223
1244
|
* @example
|
|
1224
1245
|
* ```ts
|
|
@@ -1496,20 +1517,34 @@ var isQuestion = (0, _orkestrel_contract.recordOf)({
|
|
|
1496
1517
|
* @remarks
|
|
1497
1518
|
* `observed` is required exactly where the mutation it precedes is held to it,
|
|
1498
1519
|
* absent where the destination had no bytes to record, and optional where the
|
|
1499
|
-
* comparison may not have been made.
|
|
1520
|
+
* comparison may not have been made. Planned findings require `ownership`;
|
|
1521
|
+
* foreign findings forbid it because no artifact was planned for their path.
|
|
1522
|
+
*
|
|
1523
|
+
* That is the whole claim. This guard proves the shape a reader may destructure
|
|
1524
|
+
* and nothing about whether the verdict is one an audit could have reached: the
|
|
1525
|
+
* correlation between `ownership`, `drift`, and `observed` belongs to
|
|
1526
|
+
* {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
|
|
1500
1527
|
*/
|
|
1501
1528
|
var isFinding = (0, _orkestrel_contract.unionOf)((0, _orkestrel_contract.recordOf)({
|
|
1502
1529
|
path: isPath,
|
|
1503
1530
|
group: isGroup,
|
|
1504
|
-
|
|
1531
|
+
ownership: (0, _orkestrel_contract.literalOf)("content", "presence", "birth"),
|
|
1532
|
+
drift: (0, _orkestrel_contract.literalOf)("stale"),
|
|
1533
|
+
observed: isHex
|
|
1534
|
+
}), (0, _orkestrel_contract.recordOf)({
|
|
1535
|
+
path: isPath,
|
|
1536
|
+
group: isGroup,
|
|
1537
|
+
drift: (0, _orkestrel_contract.literalOf)("foreign"),
|
|
1505
1538
|
observed: isHex
|
|
1506
1539
|
}), (0, _orkestrel_contract.recordOf)({
|
|
1507
1540
|
path: isPath,
|
|
1508
1541
|
group: isGroup,
|
|
1542
|
+
ownership: (0, _orkestrel_contract.literalOf)("content", "presence", "birth"),
|
|
1509
1543
|
drift: (0, _orkestrel_contract.literalOf)("missing")
|
|
1510
1544
|
}), (0, _orkestrel_contract.recordOf)({
|
|
1511
1545
|
path: isPath,
|
|
1512
1546
|
group: isGroup,
|
|
1547
|
+
ownership: (0, _orkestrel_contract.literalOf)("content", "presence", "birth"),
|
|
1513
1548
|
drift: (0, _orkestrel_contract.literalOf)("aligned"),
|
|
1514
1549
|
observed: isHex
|
|
1515
1550
|
}, ["observed"]));
|
|
@@ -1518,10 +1553,11 @@ var isFinding = (0, _orkestrel_contract.unionOf)((0, _orkestrel_contract.recordO
|
|
|
1518
1553
|
*
|
|
1519
1554
|
* @remarks
|
|
1520
1555
|
* An audit reaches the writer and the destructive verb, so it is guarded as
|
|
1521
|
-
* strictly as the plan beside it.
|
|
1556
|
+
* strictly as the plan beside it. Findings use the sum of the two producer
|
|
1557
|
+
* bounds: one per planned artifact, then one per unplanned snapshot path.
|
|
1522
1558
|
*/
|
|
1523
1559
|
var isAudit = (0, _orkestrel_contract.recordOf)({
|
|
1524
|
-
findings: (0, _orkestrel_contract.andOf)(
|
|
1560
|
+
findings: (0, _orkestrel_contract.andOf)((value) => (0, _orkestrel_contract.holds)(() => (0, _orkestrel_contract.isArray)(value) && value.length <= 2e3), (0, _orkestrel_contract.arrayOf)(isFinding)),
|
|
1525
1561
|
questions: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isQuestion))
|
|
1526
1562
|
});
|
|
1527
1563
|
/**
|
|
@@ -2111,6 +2147,23 @@ function inferDrift(artifact, observed) {
|
|
|
2111
2147
|
return observed === artifactToHex(artifact) ? "aligned" : "stale";
|
|
2112
2148
|
}
|
|
2113
2149
|
/**
|
|
2150
|
+
* Test whether {@link inferDrift} could have produced a finding for an ownership.
|
|
2151
|
+
*
|
|
2152
|
+
* @param ownership - What scaffold claims at the planned path.
|
|
2153
|
+
* @param finding - The audit verdict to test.
|
|
2154
|
+
* @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
|
|
2155
|
+
*
|
|
2156
|
+
* @remarks
|
|
2157
|
+
* This predicate keeps the comparison law beside the reachability law it
|
|
2158
|
+
* restates. A mutation uses it so a refusal can distinguish an impossible
|
|
2159
|
+
* verdict from a target that genuinely moved after its audit.
|
|
2160
|
+
*/
|
|
2161
|
+
function matchesDriftReachability(ownership, finding) {
|
|
2162
|
+
if (finding.drift === "aligned") return ownership === "birth" || finding.observed !== void 0;
|
|
2163
|
+
if (finding.drift === "missing") return ownership !== "birth";
|
|
2164
|
+
return finding.drift === "stale" && ownership === "content";
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2114
2167
|
* Project a plan into its tally by artifact origin.
|
|
2115
2168
|
*
|
|
2116
2169
|
* @param plan - The plan to summarize.
|
|
@@ -2538,10 +2591,9 @@ function srcToExports(src) {
|
|
|
2538
2591
|
*
|
|
2539
2592
|
* @example
|
|
2540
2593
|
* ```ts
|
|
2541
|
-
* import
|
|
2542
|
-
* import { blueprintToDevDependencies } from '@orkestrel/scaffold'
|
|
2594
|
+
* import { blueprintToDevDependencies, createBlueprint } from '@orkestrel/scaffold'
|
|
2543
2595
|
*
|
|
2544
|
-
*
|
|
2596
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2545
2597
|
*
|
|
2546
2598
|
* blueprintToDevDependencies(blueprint).typescript // the shared TypeScript pin
|
|
2547
2599
|
* ```
|
|
@@ -2552,7 +2604,7 @@ function blueprintToDevDependencies(blueprint) {
|
|
|
2552
2604
|
...blueprint.src.includes("browser") ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
|
|
2553
2605
|
...blueprint.app.length > 0 ? APP_DEV_DEPENDENCIES : {},
|
|
2554
2606
|
...blueprint.app.includes("browser") ? APP_BROWSER_DEV_DEPENDENCIES : {},
|
|
2555
|
-
...blueprint.showcase ? SHOWCASE_DEV_DEPENDENCIES : {},
|
|
2607
|
+
...blueprint.showcase && blueprint.app.includes("browser") ? SHOWCASE_DEV_DEPENDENCIES : {},
|
|
2556
2608
|
...blueprint.app.includes("server") ? APP_SERVER_DEV_DEPENDENCIES : {}
|
|
2557
2609
|
};
|
|
2558
2610
|
for (const extra of blueprint.extras) merged[extra.name] = extra.range;
|
|
@@ -2581,10 +2633,9 @@ function blueprintToDevDependencies(blueprint) {
|
|
|
2581
2633
|
*
|
|
2582
2634
|
* @example
|
|
2583
2635
|
* ```ts
|
|
2584
|
-
* import
|
|
2585
|
-
* import { blueprintToScripts } from '@orkestrel/scaffold'
|
|
2636
|
+
* import { blueprintToScripts, createBlueprint } from '@orkestrel/scaffold'
|
|
2586
2637
|
*
|
|
2587
|
-
*
|
|
2638
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2588
2639
|
*
|
|
2589
2640
|
* blueprintToScripts(blueprint)['format:check'] // 'oxfmt --config .oxfmtrc.json --check .'
|
|
2590
2641
|
* ```
|
|
@@ -2700,10 +2751,9 @@ function blueprintToScripts(blueprint) {
|
|
|
2700
2751
|
*
|
|
2701
2752
|
* @example
|
|
2702
2753
|
* ```ts
|
|
2703
|
-
* import
|
|
2704
|
-
* import { blueprintToManifest } from '@orkestrel/scaffold'
|
|
2754
|
+
* import { blueprintToManifest, createBlueprint } from '@orkestrel/scaffold'
|
|
2705
2755
|
*
|
|
2706
|
-
*
|
|
2756
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2707
2757
|
*
|
|
2708
2758
|
* blueprintToManifest(blueprint).endsWith('}\n') // true
|
|
2709
2759
|
* ```
|
|
@@ -2779,12 +2829,11 @@ function blueprintToManifest(blueprint) {
|
|
|
2779
2829
|
*
|
|
2780
2830
|
* @example
|
|
2781
2831
|
* ```ts
|
|
2782
|
-
* import
|
|
2783
|
-
* import { blueprintToMachinery } from '@orkestrel/scaffold'
|
|
2832
|
+
* import { blueprintToMachinery, createBlueprint } from '@orkestrel/scaffold'
|
|
2784
2833
|
*
|
|
2785
|
-
*
|
|
2834
|
+
* const blueprint = createBlueprint('router', { app: ['browser'] })
|
|
2786
2835
|
*
|
|
2787
|
-
* blueprintToMachinery(blueprint).vue // true
|
|
2836
|
+
* blueprintToMachinery(blueprint).vue // true
|
|
2788
2837
|
* ```
|
|
2789
2838
|
*/
|
|
2790
2839
|
function blueprintToMachinery(blueprint) {
|
|
@@ -2804,10 +2853,9 @@ function blueprintToMachinery(blueprint) {
|
|
|
2804
2853
|
*
|
|
2805
2854
|
* @example
|
|
2806
2855
|
* ```ts
|
|
2807
|
-
* import
|
|
2808
|
-
* import { blueprintToRootTsconfig } from '@orkestrel/scaffold'
|
|
2856
|
+
* import { blueprintToRootTsconfig, createBlueprint } from '@orkestrel/scaffold'
|
|
2809
2857
|
*
|
|
2810
|
-
*
|
|
2858
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2811
2859
|
*
|
|
2812
2860
|
* blueprintToRootTsconfig(blueprint).startsWith('{') // true
|
|
2813
2861
|
* ```
|
|
@@ -2832,10 +2880,9 @@ function blueprintToRootTsconfig(blueprint) {
|
|
|
2832
2880
|
*
|
|
2833
2881
|
* @example
|
|
2834
2882
|
* ```ts
|
|
2835
|
-
* import
|
|
2836
|
-
* import { blueprintToRootVite } from '@orkestrel/scaffold'
|
|
2883
|
+
* import { blueprintToRootVite, createBlueprint } from '@orkestrel/scaffold'
|
|
2837
2884
|
*
|
|
2838
|
-
*
|
|
2885
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2839
2886
|
*
|
|
2840
2887
|
* blueprintToRootVite(blueprint).includes('defineConfig') // true
|
|
2841
2888
|
* ```
|
|
@@ -2865,8 +2912,7 @@ function blueprintToRootVite(blueprint) {
|
|
|
2865
2912
|
const core = blueprint.src.includes("core");
|
|
2866
2913
|
factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.src.server, {
|
|
2867
2914
|
external: core ? `external: (id: string) =>
|
|
2868
|
-
id === '@src/core' || id.startsWith('node:') || id.startsWith('@orkestrel/'),` :
|
|
2869
|
-
id.startsWith('node:') || id.startsWith('@orkestrel/'),`,
|
|
2915
|
+
id === '@src/core' || id.startsWith('node:') || id.startsWith('@orkestrel/'),` : "external: (id: string) => id.startsWith('node:') || id.startsWith('@orkestrel/'),",
|
|
2870
2916
|
output: core ? `\t\t\t\t\toutput: [
|
|
2871
2917
|
{
|
|
2872
2918
|
format: 'es',
|
|
@@ -2892,28 +2938,35 @@ function blueprintToRootVite(blueprint) {
|
|
|
2892
2938
|
projects.push("appCore");
|
|
2893
2939
|
}
|
|
2894
2940
|
if (blueprint.app.includes("browser")) {
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
},
|
|
2912
|
-
},
|
|
2941
|
+
const showcasePlugins = machinery.showcase ? ` const showcasePlugins: PluginOption[] = showcase
|
|
2942
|
+
? [
|
|
2943
|
+
viteSingleFile({
|
|
2944
|
+
removeViteModuleLoader: true,
|
|
2945
|
+
useRecommendedBuildConfig: true,
|
|
2946
|
+
}),
|
|
2947
|
+
{
|
|
2948
|
+
name: 'orkestrel-showcase-html',
|
|
2949
|
+
transformIndexHtml: {
|
|
2950
|
+
order: 'post',
|
|
2951
|
+
handler(html) {
|
|
2952
|
+
const stamp = new Date().toISOString()
|
|
2953
|
+
return html.replace(
|
|
2954
|
+
'</head>',
|
|
2955
|
+
'\t\t<meta name="build-id" content="' + stamp + '" />\\n\t</head>',
|
|
2956
|
+
)
|
|
2913
2957
|
},
|
|
2914
|
-
|
|
2915
|
-
|
|
2958
|
+
},
|
|
2959
|
+
},
|
|
2960
|
+
]
|
|
2961
|
+
: []
|
|
2916
2962
|
` : "";
|
|
2963
|
+
const plugins = machinery.showcase ? `\t\tplugins: [
|
|
2964
|
+
outputBoundary(output),
|
|
2965
|
+
environmentBoundary('app/browser'),
|
|
2966
|
+
vue(),
|
|
2967
|
+
...showcasePlugins,
|
|
2968
|
+
],
|
|
2969
|
+
` : " plugins: [outputBoundary(output), environmentBoundary('app/browser'), vue()],\n";
|
|
2917
2970
|
const showcaseBuild = machinery.showcase ? `\t\t\t...(showcase
|
|
2918
2971
|
? {
|
|
2919
2972
|
cssMinify: 'lightningcss',
|
|
@@ -2932,11 +2985,12 @@ export function appShowcase(...options: never[]): UserConfig {
|
|
|
2932
2985
|
}
|
|
2933
2986
|
` : "";
|
|
2934
2987
|
factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.app.browser, {
|
|
2935
|
-
|
|
2988
|
+
plugins,
|
|
2989
|
+
showcasePlugins,
|
|
2936
2990
|
showcaseBuild,
|
|
2937
2991
|
showcaseFactory
|
|
2938
2992
|
}));
|
|
2939
|
-
projects.push("appBrowser");
|
|
2993
|
+
projects.push("appBrowser()");
|
|
2940
2994
|
}
|
|
2941
2995
|
if (blueprint.app.includes("server")) {
|
|
2942
2996
|
factories.push(CONFIG_TEMPLATES.factories.app.server);
|
|
@@ -2957,9 +3011,13 @@ export function appShowcase(...options: never[]): UserConfig {
|
|
|
2957
3011
|
const projectRows = `\t\tprojects: [
|
|
2958
3012
|
${projects.map((project) => `\t\t\t${project},`).join("\n")}
|
|
2959
3013
|
\t],`;
|
|
3014
|
+
const body = `${factories.join("\n")}\n`;
|
|
3015
|
+
const boundaries = ["environmentBoundary", "outputBoundary"].filter((boundary) => body.includes(boundary));
|
|
2960
3016
|
return (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.root.vite, {
|
|
3017
|
+
viteTypes: machinery.showcase ? "PluginOption, UserConfig" : "UserConfig",
|
|
2961
3018
|
imports: imports.length === 0 ? "" : `${imports.join("\n")}\n`,
|
|
2962
|
-
|
|
3019
|
+
helpers: boundaries.length === 0 ? "" : `import { ${boundaries.join(", ")} } from './configs/helpers.js'\n`,
|
|
3020
|
+
factories: body,
|
|
2963
3021
|
projects: projectRows
|
|
2964
3022
|
});
|
|
2965
3023
|
}
|
|
@@ -2971,10 +3029,9 @@ ${projects.map((project) => `\t\t\t${project},`).join("\n")}
|
|
|
2971
3029
|
*
|
|
2972
3030
|
* @example
|
|
2973
3031
|
* ```ts
|
|
2974
|
-
* import
|
|
2975
|
-
* import { blueprintToConfigArtifacts } from '@orkestrel/scaffold'
|
|
3032
|
+
* import { blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
2976
3033
|
*
|
|
2977
|
-
*
|
|
3034
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
2978
3035
|
*
|
|
2979
3036
|
* blueprintToConfigArtifacts(blueprint)[0]?.path // 'tsconfig.json'
|
|
2980
3037
|
* ```
|
|
@@ -3000,7 +3057,8 @@ function blueprintToConfigArtifacts(blueprint) {
|
|
|
3000
3057
|
else if (path === "configs/src/tsconfig.browser.json") content = CONFIG_TEMPLATES.tsconfigs.src.browser;
|
|
3001
3058
|
else if (path === "configs/src/vite.server.config.ts") {
|
|
3002
3059
|
const packageName = serializeTypeScriptString(`@orkestrel/${blueprint.name}`);
|
|
3003
|
-
const
|
|
3060
|
+
const joined = `\t\t\t\t\t\t? content.replaceAll(/(?:\\.\\.\\/)+core\\/index\\.ts/g, ${packageName})`;
|
|
3061
|
+
const replacement = joined.replaceAll(" ", " ").length <= 100 ? joined : [
|
|
3004
3062
|
" ? content.replaceAll(",
|
|
3005
3063
|
" /(?:\\.\\.\\/)+core\\/index\\.ts/g,",
|
|
3006
3064
|
`\t\t\t\t\t\t\t\t${packageName},`,
|
|
@@ -3065,7 +3123,7 @@ ${paths.join("\n")}
|
|
|
3065
3123
|
content
|
|
3066
3124
|
});
|
|
3067
3125
|
}
|
|
3068
|
-
if (blueprint.showcase) artifacts.push({
|
|
3126
|
+
if (blueprint.showcase && blueprint.app.includes("browser")) artifacts.push({
|
|
3069
3127
|
path: SHOWCASE_CONFIG_PATH,
|
|
3070
3128
|
group: "configs",
|
|
3071
3129
|
ownership: "content",
|
|
@@ -3082,16 +3140,18 @@ ${paths.join("\n")}
|
|
|
3082
3140
|
* @returns Empty published barrels, selected application entries, and the optional bin entry.
|
|
3083
3141
|
*
|
|
3084
3142
|
* @remarks
|
|
3085
|
-
* The barrels and
|
|
3086
|
-
* entity is too easy to mistake for package implementation, so the
|
|
3087
|
-
* establishes only the selected environment boundaries.
|
|
3143
|
+
* The barrels and every runtime entry intentionally hold nothing. A generated
|
|
3144
|
+
* sample entity is too easy to mistake for package implementation, so the
|
|
3145
|
+
* scaffold establishes only the selected environment boundaries. An application
|
|
3146
|
+
* entry is empty for the same reason the bin entry is, and because the vendored
|
|
3147
|
+
* lint config refuses an unassigned import outside a stylesheet, so the entry
|
|
3148
|
+
* cannot start by importing its barrel for effect either.
|
|
3088
3149
|
*
|
|
3089
3150
|
* @example
|
|
3090
3151
|
* ```ts
|
|
3091
|
-
* import
|
|
3092
|
-
* import { blueprintToSourceArtifacts } from '@orkestrel/scaffold'
|
|
3152
|
+
* import { blueprintToSourceArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
3093
3153
|
*
|
|
3094
|
-
*
|
|
3154
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
3095
3155
|
*
|
|
3096
3156
|
* blueprintToSourceArtifacts(blueprint).every(({ group }) => group === 'source') // true
|
|
3097
3157
|
* ```
|
|
@@ -3121,7 +3181,7 @@ function blueprintToSourceArtifacts(blueprint) {
|
|
|
3121
3181
|
ownership: "birth",
|
|
3122
3182
|
origin: "template",
|
|
3123
3183
|
environment,
|
|
3124
|
-
content: ARTIFACT_TEMPLATES.source.
|
|
3184
|
+
content: ARTIFACT_TEMPLATES.source.empty
|
|
3125
3185
|
}, {
|
|
3126
3186
|
path: "app/browser/index.html",
|
|
3127
3187
|
group: "source",
|
|
@@ -3136,7 +3196,7 @@ function blueprintToSourceArtifacts(blueprint) {
|
|
|
3136
3196
|
ownership: "birth",
|
|
3137
3197
|
origin: "template",
|
|
3138
3198
|
environment,
|
|
3139
|
-
content: ARTIFACT_TEMPLATES.source.
|
|
3199
|
+
content: ARTIFACT_TEMPLATES.source.empty
|
|
3140
3200
|
});
|
|
3141
3201
|
}
|
|
3142
3202
|
if (blueprint.bin) artifacts.push({
|
|
@@ -3164,10 +3224,9 @@ function blueprintToSourceArtifacts(blueprint) {
|
|
|
3164
3224
|
*
|
|
3165
3225
|
* @example
|
|
3166
3226
|
* ```ts
|
|
3167
|
-
* import
|
|
3168
|
-
* import { blueprintToTestArtifacts } from '@orkestrel/scaffold'
|
|
3227
|
+
* import { blueprintToTestArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
3169
3228
|
*
|
|
3170
|
-
*
|
|
3229
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
3171
3230
|
*
|
|
3172
3231
|
* blueprintToTestArtifacts(blueprint)[0]?.path // 'tests/setup.ts'
|
|
3173
3232
|
* ```
|
|
@@ -3377,10 +3436,9 @@ function nameToHostArtifacts(name) {
|
|
|
3377
3436
|
*
|
|
3378
3437
|
* @example
|
|
3379
3438
|
* ```ts
|
|
3380
|
-
* import
|
|
3381
|
-
* import { applyOverrides } from '@orkestrel/scaffold'
|
|
3439
|
+
* import { applyOverrides, blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
3382
3440
|
*
|
|
3383
|
-
*
|
|
3441
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
3384
3442
|
*
|
|
3385
3443
|
* applyOverrides(artifacts, [{ path: 'README.md', content: '# Title\n' }])
|
|
3386
3444
|
* ```
|
|
@@ -3418,12 +3476,11 @@ function applyOverrides(artifacts, overrides) {
|
|
|
3418
3476
|
*
|
|
3419
3477
|
* @example
|
|
3420
3478
|
* ```ts
|
|
3421
|
-
* import
|
|
3422
|
-
* import { planToHash } from '@orkestrel/scaffold'
|
|
3479
|
+
* import { createBlueprint, createCompiler, planToHash } from '@orkestrel/scaffold'
|
|
3423
3480
|
*
|
|
3424
|
-
*
|
|
3481
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
3425
3482
|
*
|
|
3426
|
-
* planToHash(plan)?.length // 16
|
|
3483
|
+
* plan === undefined ? undefined : planToHash(plan)?.length // 16
|
|
3427
3484
|
* ```
|
|
3428
3485
|
*/
|
|
3429
3486
|
function planToHash(plan) {
|
|
@@ -3441,13 +3498,16 @@ function planToHash(plan) {
|
|
|
3441
3498
|
* @param artifact - The planned artifact.
|
|
3442
3499
|
* @param observed - The destination's exact bytes as hexadecimal; absent when
|
|
3443
3500
|
* the destination holds no file.
|
|
3444
|
-
* @returns The finding, carrying
|
|
3501
|
+
* @returns The finding, carrying the artifact's ownership and `observed`
|
|
3502
|
+
* exactly where bytes were read.
|
|
3445
3503
|
*
|
|
3446
3504
|
* @remarks
|
|
3447
3505
|
* The comparison itself is {@link inferDrift}'s, so ownership decides it here
|
|
3448
3506
|
* exactly as it does everywhere else. This adds only the shape: a missing
|
|
3449
3507
|
* destination has no bytes to record, and every other verdict records the bytes
|
|
3450
3508
|
* it was given, which is the precondition the mutation that follows is held to.
|
|
3509
|
+
* Ownership is copied rather than inferred from drift because aligned findings
|
|
3510
|
+
* span all three ownership tiers.
|
|
3451
3511
|
*
|
|
3452
3512
|
* `foreign` is not answerable here, because it describes a path no artifact was
|
|
3453
3513
|
* planned for.
|
|
@@ -3459,29 +3519,34 @@ function planToHash(plan) {
|
|
|
3459
3519
|
* artifactToFinding(
|
|
3460
3520
|
* { path: 'README.md', group: 'docs', ownership: 'content', origin: 'computed', content: 'hi\n' },
|
|
3461
3521
|
* '6279650a',
|
|
3462
|
-
* ) // { path: 'README.md', group: 'docs', drift: 'stale', observed: '6279650a' }
|
|
3522
|
+
* ) // { path: 'README.md', group: 'docs', ownership: 'content', drift: 'stale', observed: '6279650a' }
|
|
3463
3523
|
* ```
|
|
3464
3524
|
*/
|
|
3465
3525
|
function artifactToFinding(artifact, observed) {
|
|
3466
3526
|
const path = artifact.path;
|
|
3467
3527
|
const group = artifact.group;
|
|
3528
|
+
const ownership = artifact.ownership;
|
|
3468
3529
|
if (observed === void 0) return inferDrift(artifact) === "aligned" ? {
|
|
3469
3530
|
path,
|
|
3470
3531
|
group,
|
|
3532
|
+
ownership,
|
|
3471
3533
|
drift: "aligned"
|
|
3472
3534
|
} : {
|
|
3473
3535
|
path,
|
|
3474
3536
|
group,
|
|
3537
|
+
ownership,
|
|
3475
3538
|
drift: "missing"
|
|
3476
3539
|
};
|
|
3477
3540
|
return inferDrift(artifact, observed) === "stale" ? {
|
|
3478
3541
|
path,
|
|
3479
3542
|
group,
|
|
3543
|
+
ownership,
|
|
3480
3544
|
drift: "stale",
|
|
3481
3545
|
observed
|
|
3482
3546
|
} : {
|
|
3483
3547
|
path,
|
|
3484
3548
|
group,
|
|
3549
|
+
ownership,
|
|
3485
3550
|
drift: "aligned",
|
|
3486
3551
|
observed
|
|
3487
3552
|
};
|
|
@@ -3505,12 +3570,11 @@ function artifactToFinding(artifact, observed) {
|
|
|
3505
3570
|
*
|
|
3506
3571
|
* @example
|
|
3507
3572
|
* ```ts
|
|
3508
|
-
* import
|
|
3509
|
-
* import { planToFindings } from '@orkestrel/scaffold'
|
|
3573
|
+
* import { createBlueprint, createCompiler, planToFindings } from '@orkestrel/scaffold'
|
|
3510
3574
|
*
|
|
3511
|
-
*
|
|
3575
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
3512
3576
|
*
|
|
3513
|
-
* planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
3577
|
+
* plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
3514
3578
|
* ```
|
|
3515
3579
|
*/
|
|
3516
3580
|
function planToFindings(plan, current) {
|
|
@@ -3594,8 +3658,8 @@ function dependenciesToQuestions(dependencies, field, name, range) {
|
|
|
3594
3658
|
* Measure a blueprint against every law its own fields decide.
|
|
3595
3659
|
*
|
|
3596
3660
|
* @param blueprint - The workspace specification.
|
|
3597
|
-
* @returns One
|
|
3598
|
-
*
|
|
3661
|
+
* @returns One question per rejected field, in blueprint field order, with the
|
|
3662
|
+
* rules that span several fields last.
|
|
3599
3663
|
*
|
|
3600
3664
|
* @remarks
|
|
3601
3665
|
* Only the laws a blueprint answers alone are here. The structural record and
|
|
@@ -3606,19 +3670,25 @@ function dependenciesToQuestions(dependencies, field, name, range) {
|
|
|
3606
3670
|
* a drafted plan belong to {@link artifactsToQuestions} and
|
|
3607
3671
|
* {@link overridesToQuestions}.
|
|
3608
3672
|
*
|
|
3609
|
-
*
|
|
3610
|
-
*
|
|
3611
|
-
*
|
|
3612
|
-
*
|
|
3673
|
+
* A question blocks when it describes a workspace this package cannot generate.
|
|
3674
|
+
* Three do not, because each describes a workspace it can describe honestly and
|
|
3675
|
+
* should not create: a published axis of several environments without core, whose
|
|
3676
|
+
* manifest names a core build the workspace never runs, and a structural flag
|
|
3677
|
+
* whose required axis is absent, which emits nothing. Blocking those closed the
|
|
3678
|
+
* gate for every verb, and the verbs that read an existing workspace need the
|
|
3679
|
+
* plan the gate refused. The caller that chooses the shape refuses the advisory;
|
|
3680
|
+
* the callers that read one report it.
|
|
3681
|
+
*
|
|
3682
|
+
* An environment question carries `ENVIRONMENTS` as its candidates, so a caller
|
|
3683
|
+
* reads the accepted values from the question instead of from the documentation.
|
|
3613
3684
|
*
|
|
3614
3685
|
* @example
|
|
3615
3686
|
* ```ts
|
|
3616
|
-
* import
|
|
3617
|
-
* import { blueprintToQuestions } from '@orkestrel/scaffold'
|
|
3687
|
+
* import { blueprintToQuestions, createBlueprint } from '@orkestrel/scaffold'
|
|
3618
3688
|
*
|
|
3619
|
-
*
|
|
3689
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
3620
3690
|
*
|
|
3621
|
-
* blueprintToQuestions(blueprint).length === 0 // true
|
|
3691
|
+
* blueprintToQuestions(blueprint).length === 0 // true
|
|
3622
3692
|
* ```
|
|
3623
3693
|
*/
|
|
3624
3694
|
function blueprintToQuestions(blueprint) {
|
|
@@ -3644,6 +3714,12 @@ function blueprintToQuestions(blueprint) {
|
|
|
3644
3714
|
blocking: true,
|
|
3645
3715
|
candidates: ENVIRONMENTS
|
|
3646
3716
|
});
|
|
3717
|
+
if (blueprint.src.length > 1 && !blueprint.src.includes("core")) questions.push({
|
|
3718
|
+
field: "src",
|
|
3719
|
+
message: "Several published environments put core at the package root, so this manifest names a core build the workspace never runs. Declare core on src, or publish one environment.",
|
|
3720
|
+
blocking: false,
|
|
3721
|
+
candidates: ENVIRONMENTS
|
|
3722
|
+
});
|
|
3647
3723
|
for (const environment of ENVIRONMENTS) {
|
|
3648
3724
|
if (blueprint.src.filter((declared) => declared === environment).length > 1) questions.push({
|
|
3649
3725
|
field: "src",
|
|
@@ -3658,11 +3734,10 @@ function blueprintToQuestions(blueprint) {
|
|
|
3658
3734
|
candidates: ENVIRONMENTS
|
|
3659
3735
|
});
|
|
3660
3736
|
}
|
|
3661
|
-
if (blueprint.
|
|
3662
|
-
field: "
|
|
3663
|
-
message: "
|
|
3664
|
-
blocking:
|
|
3665
|
-
candidates: ENVIRONMENTS
|
|
3737
|
+
if (blueprint.integration && blueprint.src.length === 0) questions.push({
|
|
3738
|
+
field: "integration",
|
|
3739
|
+
message: "integration projects a published src, and this workspace declares none, so it emits nothing.",
|
|
3740
|
+
blocking: false
|
|
3666
3741
|
});
|
|
3667
3742
|
const services = /* @__PURE__ */ new Set();
|
|
3668
3743
|
for (const service of blueprint.services) {
|
|
@@ -3678,6 +3753,11 @@ function blueprintToQuestions(blueprint) {
|
|
|
3678
3753
|
});
|
|
3679
3754
|
services.add(service);
|
|
3680
3755
|
}
|
|
3756
|
+
if (blueprint.showcase && !blueprint.app.includes("browser")) questions.push({
|
|
3757
|
+
field: "showcase",
|
|
3758
|
+
message: "showcase projects a browser app, and this workspace declares none, so it emits nothing.",
|
|
3759
|
+
blocking: false
|
|
3760
|
+
});
|
|
3681
3761
|
questions.push(...dependenciesToQuestions(blueprint.dependencies, "dependencies", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.peers, "peers", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.extras, "extras", EXTRA_NAME_PATTERN, EXTRA_RANGE_PATTERN));
|
|
3682
3762
|
const lists = [
|
|
3683
3763
|
["dependencies", blueprint.dependencies],
|
|
@@ -3726,12 +3806,11 @@ function blueprintToQuestions(blueprint) {
|
|
|
3726
3806
|
*
|
|
3727
3807
|
* @example
|
|
3728
3808
|
* ```ts
|
|
3729
|
-
* import
|
|
3730
|
-
* import { artifactsToQuestions } from '@orkestrel/scaffold'
|
|
3809
|
+
* import { artifactsToQuestions, blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
3731
3810
|
*
|
|
3732
|
-
*
|
|
3811
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
3733
3812
|
*
|
|
3734
|
-
* artifactsToQuestions(artifacts).length === 0 // true
|
|
3813
|
+
* artifactsToQuestions(artifacts).length === 0 // true
|
|
3735
3814
|
* ```
|
|
3736
3815
|
*/
|
|
3737
3816
|
function artifactsToQuestions(artifacts) {
|
|
@@ -3783,10 +3862,9 @@ function artifactsToQuestions(artifacts) {
|
|
|
3783
3862
|
*
|
|
3784
3863
|
* @example
|
|
3785
3864
|
* ```ts
|
|
3786
|
-
* import
|
|
3787
|
-
* import { overridesToQuestions } from '@orkestrel/scaffold'
|
|
3865
|
+
* import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
|
|
3788
3866
|
*
|
|
3789
|
-
*
|
|
3867
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
3790
3868
|
*
|
|
3791
3869
|
* overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
|
|
3792
3870
|
* ```
|
|
@@ -3913,12 +3991,19 @@ var Compiler = class {
|
|
|
3913
3991
|
* carries no plan, then `compile` with the whole outcome either way, so an
|
|
3914
3992
|
* observer reads every compile from one event and the refusals from the other.
|
|
3915
3993
|
*
|
|
3994
|
+
* A plan says the blueprint can be built. It does not say the blueprint should
|
|
3995
|
+
* be created, and the questions beside it are what this compiler could not
|
|
3996
|
+
* settle. A caller creating a fresh workspace answers them first and writes
|
|
3997
|
+
* nothing while any remains, which is the rule the `new` verb applies; a caller
|
|
3998
|
+
* describing or repairing an existing target carries them through instead.
|
|
3999
|
+
* Nothing downstream repeats that check, because only the caller knows which of
|
|
4000
|
+
* the two it is.
|
|
4001
|
+
*
|
|
3916
4002
|
* @example
|
|
3917
4003
|
* ```ts
|
|
3918
|
-
* import
|
|
3919
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
4004
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
3920
4005
|
*
|
|
3921
|
-
*
|
|
4006
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
3922
4007
|
*
|
|
3923
4008
|
* createCompiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
|
|
3924
4009
|
* ```
|
|
@@ -3946,14 +4031,19 @@ var Compiler = class {
|
|
|
3946
4031
|
* reports no findings and carries the questions instead. Emits `block` in that
|
|
3947
4032
|
* case, then `audit` with the verdict either way.
|
|
3948
4033
|
*
|
|
4034
|
+
* Ownership decides each verdict, not absence. A birth-owned path is never
|
|
4035
|
+
* compared and reads `aligned` against a target holding nothing, while a
|
|
4036
|
+
* content-owned path with no bytes to read is `missing`. An empty snapshot
|
|
4037
|
+
* therefore produces both verdicts rather than one.
|
|
4038
|
+
*
|
|
3949
4039
|
* @example
|
|
3950
4040
|
* ```ts
|
|
3951
|
-
* import
|
|
3952
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
4041
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
3953
4042
|
*
|
|
3954
|
-
*
|
|
4043
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
3955
4044
|
*
|
|
3956
|
-
* createCompiler().audit(blueprint, {}).findings
|
|
4045
|
+
* createCompiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
|
|
4046
|
+
* createCompiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
|
|
3957
4047
|
* ```
|
|
3958
4048
|
*/
|
|
3959
4049
|
audit(blueprint, current, groups) {
|
|
@@ -3991,7 +4081,10 @@ var Compiler = class {
|
|
|
3991
4081
|
}
|
|
3992
4082
|
#scaffold(blueprint, groups) {
|
|
3993
4083
|
const stages = [];
|
|
3994
|
-
const
|
|
4084
|
+
const draft = this.#draft(blueprint);
|
|
4085
|
+
const selected = [];
|
|
4086
|
+
for (const group of groups) for (const artifact of draft) if (artifact.group === group) selected.push(artifact);
|
|
4087
|
+
const artifacts = applyOverrides(selected, blueprint.overrides);
|
|
3995
4088
|
stages.push({
|
|
3996
4089
|
stage: "draft",
|
|
3997
4090
|
input: {
|
|
@@ -4000,7 +4093,7 @@ var Compiler = class {
|
|
|
4000
4093
|
},
|
|
4001
4094
|
output: artifacts
|
|
4002
4095
|
});
|
|
4003
|
-
const questions = this.#gate(blueprint, artifacts);
|
|
4096
|
+
const questions = this.#gate(blueprint, draft, artifacts);
|
|
4004
4097
|
const blocking = questions.filter((question) => question.blocking);
|
|
4005
4098
|
if (blocking.length > 0) {
|
|
4006
4099
|
stages.push({
|
|
@@ -4069,8 +4162,8 @@ var Compiler = class {
|
|
|
4069
4162
|
stages
|
|
4070
4163
|
};
|
|
4071
4164
|
}
|
|
4072
|
-
#draft(blueprint
|
|
4073
|
-
|
|
4165
|
+
#draft(blueprint) {
|
|
4166
|
+
return [
|
|
4074
4167
|
{
|
|
4075
4168
|
path: "package.json",
|
|
4076
4169
|
group: "manifest",
|
|
@@ -4086,14 +4179,11 @@ var Compiler = class {
|
|
|
4086
4179
|
...blueprintToOrchestrationArtifacts(blueprint),
|
|
4087
4180
|
...nameToHostArtifacts(blueprint.name)
|
|
4088
4181
|
];
|
|
4089
|
-
const covered = [];
|
|
4090
|
-
for (const group of groups) for (const artifact of drafted) if (artifact.group === group) covered.push(artifact);
|
|
4091
|
-
return applyOverrides(covered, blueprint.overrides);
|
|
4092
4182
|
}
|
|
4093
|
-
#gate(blueprint, artifacts) {
|
|
4183
|
+
#gate(blueprint, draft, artifacts) {
|
|
4094
4184
|
return [
|
|
4095
4185
|
...blueprintToQuestions(blueprint),
|
|
4096
|
-
...overridesToQuestions(blueprint.overrides,
|
|
4186
|
+
...overridesToQuestions(blueprint.overrides, draft),
|
|
4097
4187
|
...artifactsToQuestions(artifacts)
|
|
4098
4188
|
];
|
|
4099
4189
|
}
|
|
@@ -4215,6 +4305,7 @@ exports.DEFAULT_VERSION = DEFAULT_VERSION;
|
|
|
4215
4305
|
exports.DEPENDENCY_NAME_PATTERN = DEPENDENCY_NAME_PATTERN;
|
|
4216
4306
|
exports.ENGINES_PATTERN = ENGINES_PATTERN;
|
|
4217
4307
|
exports.ENVIRONMENTS = ENVIRONMENTS;
|
|
4308
|
+
exports.EXECUTABLE_PATHS = EXECUTABLE_PATHS;
|
|
4218
4309
|
exports.EXTRA_NAME_PATTERN = EXTRA_NAME_PATTERN;
|
|
4219
4310
|
exports.EXTRA_RANGE_PATTERN = EXTRA_RANGE_PATTERN;
|
|
4220
4311
|
exports.GLOBAL_SETUP_PATH = GLOBAL_SETUP_PATH;
|
|
@@ -4226,6 +4317,7 @@ exports.INTEGRATION_TEST_PATH = INTEGRATION_TEST_PATH;
|
|
|
4226
4317
|
exports.INVALID_PATH_CHARACTER_PATTERN = INVALID_PATH_CHARACTER_PATTERN;
|
|
4227
4318
|
exports.MAX_ARTIFACT_BYTES = MAX_ARTIFACT_BYTES;
|
|
4228
4319
|
exports.MAX_ARTIFACT_HEX_LENGTH = MAX_ARTIFACT_HEX_LENGTH;
|
|
4320
|
+
exports.MAX_AUDIT_FINDINGS = MAX_AUDIT_FINDINGS;
|
|
4229
4321
|
exports.MAX_COLLECTION_ITEMS = MAX_COLLECTION_ITEMS;
|
|
4230
4322
|
exports.MAX_DEPENDENCY_NAME_LENGTH = MAX_DEPENDENCY_NAME_LENGTH;
|
|
4231
4323
|
exports.MAX_MANIFEST_BYTES = MAX_MANIFEST_BYTES;
|
|
@@ -4298,6 +4390,7 @@ exports.isScaffoldError = isScaffoldError;
|
|
|
4298
4390
|
exports.isSnapshot = isSnapshot;
|
|
4299
4391
|
exports.manifestToDependencies = manifestToDependencies;
|
|
4300
4392
|
exports.manifestToName = manifestToName;
|
|
4393
|
+
exports.matchesDriftReachability = matchesDriftReachability;
|
|
4301
4394
|
exports.matchesEngines = matchesEngines;
|
|
4302
4395
|
exports.matchesOrchestrationPath = matchesOrchestrationPath;
|
|
4303
4396
|
exports.matchesRange = matchesRange;
|