@orkestrel/scaffold 0.0.24 → 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
|
@@ -46,10 +46,9 @@ export declare interface AppDefinition {
|
|
|
46
46
|
*
|
|
47
47
|
* @example
|
|
48
48
|
* ```ts
|
|
49
|
-
* import
|
|
50
|
-
* import { applyOverrides } from '@orkestrel/scaffold'
|
|
49
|
+
* import { applyOverrides, blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
51
50
|
*
|
|
52
|
-
*
|
|
51
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
53
52
|
*
|
|
54
53
|
* applyOverrides(artifacts, [{ path: 'README.md', content: '# Title\n' }])
|
|
55
54
|
* ```
|
|
@@ -71,9 +70,13 @@ export declare type Artifact = HostArtifact | HydratedArtifact | ContentArtifact
|
|
|
71
70
|
*
|
|
72
71
|
* @remarks
|
|
73
72
|
* Builders in `compilers.ts` fill every varying span through
|
|
74
|
-
* `@orkestrel/template`. Empty barrels and setup modules are
|
|
75
|
-
* generated workspace starts with no sample domain API, while
|
|
76
|
-
* Vitest project gets a real test that proves its barrel has no
|
|
73
|
+
* `@orkestrel/template`. Empty barrels, entries, and setup modules are
|
|
74
|
+
* intentional: the generated workspace starts with no sample domain API, while
|
|
75
|
+
* each selected Vitest project gets a real test that proves its barrel has no
|
|
76
|
+
* starter exports. An entry starts empty for the same reason its barrel does,
|
|
77
|
+
* and because the vendored lint config refuses an unassigned import outside a
|
|
78
|
+
* stylesheet, so a starter `import './index.js'` would fail the workspace's own
|
|
79
|
+
* `lint:check` on the day it is written.
|
|
77
80
|
*
|
|
78
81
|
* @example
|
|
79
82
|
* ```ts
|
|
@@ -85,7 +88,6 @@ export declare type Artifact = HostArtifact | HydratedArtifact | ContentArtifact
|
|
|
85
88
|
export declare const ARTIFACT_TEMPLATES: Readonly<{
|
|
86
89
|
source: Readonly<{
|
|
87
90
|
empty: "";
|
|
88
|
-
main: "import './index.js'\n";
|
|
89
91
|
browser: "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title>Application</title>\n\t</head>\n\t<body>\n\t\t<script type=\"module\" src=\"/main.ts\"></script>\n\t</body>\n</html>\n";
|
|
90
92
|
}>;
|
|
91
93
|
tests: Readonly<{
|
|
@@ -133,12 +135,11 @@ export declare interface ArtifactBase {
|
|
|
133
135
|
*
|
|
134
136
|
* @example
|
|
135
137
|
* ```ts
|
|
136
|
-
* import
|
|
137
|
-
* import { artifactsToQuestions } from '@orkestrel/scaffold'
|
|
138
|
+
* import { artifactsToQuestions, blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
138
139
|
*
|
|
139
|
-
*
|
|
140
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
140
141
|
*
|
|
141
|
-
* artifactsToQuestions(artifacts).length === 0 // true
|
|
142
|
+
* artifactsToQuestions(artifacts).length === 0 // true
|
|
142
143
|
* ```
|
|
143
144
|
*/
|
|
144
145
|
export declare function artifactsToQuestions(artifacts: readonly Artifact[]): readonly Question[];
|
|
@@ -149,13 +150,16 @@ export declare function artifactsToQuestions(artifacts: readonly Artifact[]): re
|
|
|
149
150
|
* @param artifact - The planned artifact.
|
|
150
151
|
* @param observed - The destination's exact bytes as hexadecimal; absent when
|
|
151
152
|
* the destination holds no file.
|
|
152
|
-
* @returns The finding, carrying
|
|
153
|
+
* @returns The finding, carrying the artifact's ownership and `observed`
|
|
154
|
+
* exactly where bytes were read.
|
|
153
155
|
*
|
|
154
156
|
* @remarks
|
|
155
157
|
* The comparison itself is {@link inferDrift}'s, so ownership decides it here
|
|
156
158
|
* exactly as it does everywhere else. This adds only the shape: a missing
|
|
157
159
|
* destination has no bytes to record, and every other verdict records the bytes
|
|
158
160
|
* it was given, which is the precondition the mutation that follows is held to.
|
|
161
|
+
* Ownership is copied rather than inferred from drift because aligned findings
|
|
162
|
+
* span all three ownership tiers.
|
|
159
163
|
*
|
|
160
164
|
* `foreign` is not answerable here, because it describes a path no artifact was
|
|
161
165
|
* planned for.
|
|
@@ -167,7 +171,7 @@ export declare function artifactsToQuestions(artifacts: readonly Artifact[]): re
|
|
|
167
171
|
* artifactToFinding(
|
|
168
172
|
* { path: 'README.md', group: 'docs', ownership: 'content', origin: 'computed', content: 'hi\n' },
|
|
169
173
|
* '6279650a',
|
|
170
|
-
* ) // { path: 'README.md', group: 'docs', drift: 'stale', observed: '6279650a' }
|
|
174
|
+
* ) // { path: 'README.md', group: 'docs', ownership: 'content', drift: 'stale', observed: '6279650a' }
|
|
171
175
|
* ```
|
|
172
176
|
*/
|
|
173
177
|
export declare function artifactToFinding(artifact: Artifact, observed?: string): Finding;
|
|
@@ -205,7 +209,8 @@ export declare function artifactToHex(artifact: Artifact): string | undefined;
|
|
|
205
209
|
* @remarks
|
|
206
210
|
* A blocking question means the gate refused the blueprint, so `findings` is
|
|
207
211
|
* empty and says nothing about the target. Tallies are not stored: count
|
|
208
|
-
* `findings` by `drift`.
|
|
212
|
+
* `findings` by `drift` or `ownership`. The finding bound is the sum of the
|
|
213
|
+
* separately bounded plan artifacts and snapshot paths.
|
|
209
214
|
*/
|
|
210
215
|
export declare interface Audit {
|
|
211
216
|
readonly findings: readonly Finding[];
|
|
@@ -233,7 +238,11 @@ export declare const BIN_ENTRY_PATH = "src/bin/main.ts";
|
|
|
233
238
|
* `bin`, `integration`, `services`, `global`, and `showcase` are structural
|
|
234
239
|
* facts: each is set only when the workspace physically ships the directory or
|
|
235
240
|
* exact-case file that defines it, never because of the workspace's name and
|
|
236
|
-
* never because a sibling fact is set.
|
|
241
|
+
* never because a sibling fact is set. An axis-dependent fact projects nothing
|
|
242
|
+
* when its required axis is absent: `integration` projects only a published
|
|
243
|
+
* `src`, and `showcase` projects only a browser `app`. The gate answers that case
|
|
244
|
+
* with a non-blocking question, so a caller that set the flag learns it emitted
|
|
245
|
+
* nothing and the compile still completes.
|
|
237
246
|
*/
|
|
238
247
|
export declare interface Blueprint {
|
|
239
248
|
readonly name: string;
|
|
@@ -262,10 +271,9 @@ export declare interface Blueprint {
|
|
|
262
271
|
*
|
|
263
272
|
* @example
|
|
264
273
|
* ```ts
|
|
265
|
-
* import
|
|
266
|
-
* import { blueprintToConfigArtifacts } from '@orkestrel/scaffold'
|
|
274
|
+
* import { blueprintToConfigArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
267
275
|
*
|
|
268
|
-
*
|
|
276
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
269
277
|
*
|
|
270
278
|
* blueprintToConfigArtifacts(blueprint)[0]?.path // 'tsconfig.json'
|
|
271
279
|
* ```
|
|
@@ -298,10 +306,9 @@ export declare function blueprintToConfigArtifacts(blueprint: Blueprint): readon
|
|
|
298
306
|
*
|
|
299
307
|
* @example
|
|
300
308
|
* ```ts
|
|
301
|
-
* import
|
|
302
|
-
* import { blueprintToDevDependencies } from '@orkestrel/scaffold'
|
|
309
|
+
* import { blueprintToDevDependencies, createBlueprint } from '@orkestrel/scaffold'
|
|
303
310
|
*
|
|
304
|
-
*
|
|
311
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
305
312
|
*
|
|
306
313
|
* blueprintToDevDependencies(blueprint).typescript // the shared TypeScript pin
|
|
307
314
|
* ```
|
|
@@ -344,12 +351,11 @@ export declare function blueprintToGuideArtifacts(blueprint: Blueprint): readonl
|
|
|
344
351
|
*
|
|
345
352
|
* @example
|
|
346
353
|
* ```ts
|
|
347
|
-
* import
|
|
348
|
-
* import { blueprintToMachinery } from '@orkestrel/scaffold'
|
|
354
|
+
* import { blueprintToMachinery, createBlueprint } from '@orkestrel/scaffold'
|
|
349
355
|
*
|
|
350
|
-
*
|
|
356
|
+
* const blueprint = createBlueprint('router', { app: ['browser'] })
|
|
351
357
|
*
|
|
352
|
-
* blueprintToMachinery(blueprint).vue // true
|
|
358
|
+
* blueprintToMachinery(blueprint).vue // true
|
|
353
359
|
* ```
|
|
354
360
|
*/
|
|
355
361
|
export declare function blueprintToMachinery(blueprint: Blueprint): ViteMachinery;
|
|
@@ -382,10 +388,9 @@ export declare function blueprintToMachinery(blueprint: Blueprint): ViteMachiner
|
|
|
382
388
|
*
|
|
383
389
|
* @example
|
|
384
390
|
* ```ts
|
|
385
|
-
* import
|
|
386
|
-
* import { blueprintToManifest } from '@orkestrel/scaffold'
|
|
391
|
+
* import { blueprintToManifest, createBlueprint } from '@orkestrel/scaffold'
|
|
387
392
|
*
|
|
388
|
-
*
|
|
393
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
389
394
|
*
|
|
390
395
|
* blueprintToManifest(blueprint).endsWith('}\n') // true
|
|
391
396
|
* ```
|
|
@@ -409,8 +414,8 @@ export declare function blueprintToOrchestrationArtifacts(blueprint: Blueprint):
|
|
|
409
414
|
* Measure a blueprint against every law its own fields decide.
|
|
410
415
|
*
|
|
411
416
|
* @param blueprint - The workspace specification.
|
|
412
|
-
* @returns One
|
|
413
|
-
*
|
|
417
|
+
* @returns One question per rejected field, in blueprint field order, with the
|
|
418
|
+
* rules that span several fields last.
|
|
414
419
|
*
|
|
415
420
|
* @remarks
|
|
416
421
|
* Only the laws a blueprint answers alone are here. The structural record and
|
|
@@ -421,19 +426,25 @@ export declare function blueprintToOrchestrationArtifacts(blueprint: Blueprint):
|
|
|
421
426
|
* a drafted plan belong to {@link artifactsToQuestions} and
|
|
422
427
|
* {@link overridesToQuestions}.
|
|
423
428
|
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
429
|
+
* A question blocks when it describes a workspace this package cannot generate.
|
|
430
|
+
* Three do not, because each describes a workspace it can describe honestly and
|
|
431
|
+
* should not create: a published axis of several environments without core, whose
|
|
432
|
+
* manifest names a core build the workspace never runs, and a structural flag
|
|
433
|
+
* whose required axis is absent, which emits nothing. Blocking those closed the
|
|
434
|
+
* gate for every verb, and the verbs that read an existing workspace need the
|
|
435
|
+
* plan the gate refused. The caller that chooses the shape refuses the advisory;
|
|
436
|
+
* the callers that read one report it.
|
|
437
|
+
*
|
|
438
|
+
* An environment question carries `ENVIRONMENTS` as its candidates, so a caller
|
|
439
|
+
* reads the accepted values from the question instead of from the documentation.
|
|
428
440
|
*
|
|
429
441
|
* @example
|
|
430
442
|
* ```ts
|
|
431
|
-
* import
|
|
432
|
-
* import { blueprintToQuestions } from '@orkestrel/scaffold'
|
|
443
|
+
* import { blueprintToQuestions, createBlueprint } from '@orkestrel/scaffold'
|
|
433
444
|
*
|
|
434
|
-
*
|
|
445
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
435
446
|
*
|
|
436
|
-
* blueprintToQuestions(blueprint).length === 0 // true
|
|
447
|
+
* blueprintToQuestions(blueprint).length === 0 // true
|
|
437
448
|
* ```
|
|
438
449
|
*/
|
|
439
450
|
export declare function blueprintToQuestions(blueprint: Blueprint): readonly Question[];
|
|
@@ -446,10 +457,9 @@ export declare function blueprintToQuestions(blueprint: Blueprint): readonly Que
|
|
|
446
457
|
*
|
|
447
458
|
* @example
|
|
448
459
|
* ```ts
|
|
449
|
-
* import
|
|
450
|
-
* import { blueprintToRootTsconfig } from '@orkestrel/scaffold'
|
|
460
|
+
* import { blueprintToRootTsconfig, createBlueprint } from '@orkestrel/scaffold'
|
|
451
461
|
*
|
|
452
|
-
*
|
|
462
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
453
463
|
*
|
|
454
464
|
* blueprintToRootTsconfig(blueprint).startsWith('{') // true
|
|
455
465
|
* ```
|
|
@@ -469,10 +479,9 @@ export declare function blueprintToRootTsconfig(blueprint: Blueprint): string;
|
|
|
469
479
|
*
|
|
470
480
|
* @example
|
|
471
481
|
* ```ts
|
|
472
|
-
* import
|
|
473
|
-
* import { blueprintToRootVite } from '@orkestrel/scaffold'
|
|
482
|
+
* import { blueprintToRootVite, createBlueprint } from '@orkestrel/scaffold'
|
|
474
483
|
*
|
|
475
|
-
*
|
|
484
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
476
485
|
*
|
|
477
486
|
* blueprintToRootVite(blueprint).includes('defineConfig') // true
|
|
478
487
|
* ```
|
|
@@ -499,10 +508,9 @@ export declare function blueprintToRootVite(blueprint: Blueprint): string;
|
|
|
499
508
|
*
|
|
500
509
|
* @example
|
|
501
510
|
* ```ts
|
|
502
|
-
* import
|
|
503
|
-
* import { blueprintToScripts } from '@orkestrel/scaffold'
|
|
511
|
+
* import { blueprintToScripts, createBlueprint } from '@orkestrel/scaffold'
|
|
504
512
|
*
|
|
505
|
-
*
|
|
513
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
506
514
|
*
|
|
507
515
|
* blueprintToScripts(blueprint)['format:check'] // 'oxfmt --config .oxfmtrc.json --check .'
|
|
508
516
|
* ```
|
|
@@ -516,16 +524,18 @@ export declare function blueprintToScripts(blueprint: Blueprint): Readonly<Recor
|
|
|
516
524
|
* @returns Empty published barrels, selected application entries, and the optional bin entry.
|
|
517
525
|
*
|
|
518
526
|
* @remarks
|
|
519
|
-
* The barrels and
|
|
520
|
-
* entity is too easy to mistake for package implementation, so the
|
|
521
|
-
* establishes only the selected environment boundaries.
|
|
527
|
+
* The barrels and every runtime entry intentionally hold nothing. A generated
|
|
528
|
+
* sample entity is too easy to mistake for package implementation, so the
|
|
529
|
+
* scaffold establishes only the selected environment boundaries. An application
|
|
530
|
+
* entry is empty for the same reason the bin entry is, and because the vendored
|
|
531
|
+
* lint config refuses an unassigned import outside a stylesheet, so the entry
|
|
532
|
+
* cannot start by importing its barrel for effect either.
|
|
522
533
|
*
|
|
523
534
|
* @example
|
|
524
535
|
* ```ts
|
|
525
|
-
* import
|
|
526
|
-
* import { blueprintToSourceArtifacts } from '@orkestrel/scaffold'
|
|
536
|
+
* import { blueprintToSourceArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
527
537
|
*
|
|
528
|
-
*
|
|
538
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
529
539
|
*
|
|
530
540
|
* blueprintToSourceArtifacts(blueprint).every(({ group }) => group === 'source') // true
|
|
531
541
|
* ```
|
|
@@ -548,10 +558,9 @@ export declare function blueprintToSourceArtifacts(blueprint: Blueprint): readon
|
|
|
548
558
|
*
|
|
549
559
|
* @example
|
|
550
560
|
* ```ts
|
|
551
|
-
* import
|
|
552
|
-
* import { blueprintToTestArtifacts } from '@orkestrel/scaffold'
|
|
561
|
+
* import { blueprintToTestArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
553
562
|
*
|
|
554
|
-
*
|
|
563
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
555
564
|
*
|
|
556
565
|
* blueprintToTestArtifacts(blueprint)[0]?.path // 'tests/setup.ts'
|
|
557
566
|
* ```
|
|
@@ -759,12 +768,19 @@ export declare class Compiler implements CompilerInterface {
|
|
|
759
768
|
* carries no plan, then `compile` with the whole outcome either way, so an
|
|
760
769
|
* observer reads every compile from one event and the refusals from the other.
|
|
761
770
|
*
|
|
771
|
+
* A plan says the blueprint can be built. It does not say the blueprint should
|
|
772
|
+
* be created, and the questions beside it are what this compiler could not
|
|
773
|
+
* settle. A caller creating a fresh workspace answers them first and writes
|
|
774
|
+
* nothing while any remains, which is the rule the `new` verb applies; a caller
|
|
775
|
+
* describing or repairing an existing target carries them through instead.
|
|
776
|
+
* Nothing downstream repeats that check, because only the caller knows which of
|
|
777
|
+
* the two it is.
|
|
778
|
+
*
|
|
762
779
|
* @example
|
|
763
780
|
* ```ts
|
|
764
|
-
* import
|
|
765
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
781
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
766
782
|
*
|
|
767
|
-
*
|
|
783
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
768
784
|
*
|
|
769
785
|
* createCompiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
|
|
770
786
|
* ```
|
|
@@ -785,14 +801,19 @@ export declare class Compiler implements CompilerInterface {
|
|
|
785
801
|
* reports no findings and carries the questions instead. Emits `block` in that
|
|
786
802
|
* case, then `audit` with the verdict either way.
|
|
787
803
|
*
|
|
804
|
+
* Ownership decides each verdict, not absence. A birth-owned path is never
|
|
805
|
+
* compared and reads `aligned` against a target holding nothing, while a
|
|
806
|
+
* content-owned path with no bytes to read is `missing`. An empty snapshot
|
|
807
|
+
* therefore produces both verdicts rather than one.
|
|
808
|
+
*
|
|
788
809
|
* @example
|
|
789
810
|
* ```ts
|
|
790
|
-
* import
|
|
791
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
811
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
792
812
|
*
|
|
793
|
-
*
|
|
813
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
794
814
|
*
|
|
795
|
-
* createCompiler().audit(blueprint, {}).findings
|
|
815
|
+
* createCompiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
|
|
816
|
+
* createCompiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
|
|
796
817
|
* ```
|
|
797
818
|
*/
|
|
798
819
|
audit(blueprint: Blueprint, current: Snapshot, groups?: readonly Group[]): Audit;
|
|
@@ -941,7 +962,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
941
962
|
export declare const CONFIG_TEMPLATES: Readonly<{
|
|
942
963
|
root: Readonly<{
|
|
943
964
|
tsconfig: "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ESNext\",\n\t\t\"module\": \"ESNext\",\n\t\t\"moduleResolution\": \"bundler\",\n\t\t\"allowImportingTsExtensions\": true,\n\t\t\"lib\": [\"ESNext\", \"DOM\", \"DOM.Iterable\"],\n\t\t\"types\": [\"node\", \"vite/client\", \"vitest/globals\"],\n\t\t\"moduleDetection\": \"force\",\n\t\t\"resolveJsonModule\": true,\n\t\t\"strict\": true,\n\t\t\"verbatimModuleSyntax\": true,\n\t\t\"noUncheckedIndexedAccess\": true,\n\t\t\"noUncheckedSideEffectImports\": true,\n\t\t\"exactOptionalPropertyTypes\": true,\n\t\t\"noUnusedLocals\": true,\n\t\t\"noUnusedParameters\": true,\n\t\t\"noImplicitOverride\": true,\n\t\t\"noFallthroughCasesInSwitch\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"noEmit\": true,\n\t\t\"paths\": {\n{{paths}}\n\t\t}\n\t},\n\t\"exclude\": [\"node_modules\", \"dist\", \"tmp\"]\n}\n";
|
|
944
|
-
vite: "import type {
|
|
965
|
+
vite: "import type { {{viteTypes}} } from 'vite'\n{{imports}}import { defineConfig, mergeConfig } from 'vitest/config'\nimport tsconfig from './tsconfig.json' with { type: 'json' }\n{{helpers}}import { lstatSync, readdirSync, realpathSync } from 'node:fs'\nimport { basename, join, parse, relative, resolve as resolvePath, sep } from 'node:path'\nimport { fileURLToPath, URL } from 'node:url'\n\nexport function resolveWorkspacePath(relativePath: string): string {\n\treturn fileURLToPath(new URL(relativePath, import.meta.url))\n}\n\n// A generated root config must classify its own fixed proof without importing\n// package source, so the exact-case check stays self-contained over Node APIs.\nfunction isExactCaseFile(path: string): boolean {\n\tconst full = resolvePath(path)\n\ttry {\n\t\tconst status = lstatSync(full)\n\t\tif (!status.isFile() || status.isSymbolicLink() || status.nlink !== 1) return false\n\t\tconst root = parse(full).root\n\t\tconst segments = relative(root, full).split(sep)\n\t\tlet parent = root\n\t\tfor (const segment of segments) {\n\t\t\ttry {\n\t\t\t\tif (!readdirSync(parent).includes(segment)) return false\n\t\t\t} catch {\n\t\t\t\tif (basename(realpathSync.native(join(parent, segment))) !== segment) return false\n\t\t\t}\n\t\t\tparent = join(parent, segment)\n\t\t}\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nconst resolve = {\n\talias: Object.entries(tsconfig.compilerOptions.paths).reduce((aliases, [key, values]) => {\n\t\tconst [path] = values\n\t\tif (path === undefined) throw new Error('tsconfig path alias ' + key + ' has no target')\n\t\treturn Object.assign(aliases, { [key]: resolveWorkspacePath(path) })\n\t}, {}),\n}\n\n{{factories}}export default defineConfig({\n\tresolve,\n\ttest: {\n{{projects}}\n\t},\n})\n";
|
|
945
966
|
}>;
|
|
946
967
|
factories: Readonly<{
|
|
947
968
|
src: Readonly<{
|
|
@@ -952,7 +973,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
952
973
|
}>;
|
|
953
974
|
app: Readonly<{
|
|
954
975
|
core: "export const appCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [environmentBoundary('app/core')],\n\t\t\ttest: {\n\t\t\t\tname: { label: 'app:core', color: 'cyan' },\n\t\t\t\tinclude: ['tests/app/core/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
955
|
-
browser: "function applicationBrowser(showcase: boolean): UserConfig {\n\tconst output = showcase ? 'dist/showcase' : 'dist/app/browser'\n\treturn {\n\t\tresolve,\n
|
|
976
|
+
browser: "function applicationBrowser(showcase: boolean): UserConfig {\n\tconst output = showcase ? 'dist/showcase' : 'dist/app/browser'\n{{showcasePlugins}}\treturn {\n\t\tresolve,\n{{plugins}}\t\troot: resolveWorkspacePath('app/browser'),\n\t\tpublicDir: false,\n\t\tbuild: {\n{{showcaseBuild}}\t\t\temptyOutDir: true,\n\t\t\toutDir: resolveWorkspacePath(output),\n\t\t\trolldownOptions: { input: resolveWorkspacePath('app/browser/index.html') },\n\t\t},\n\t\ttest: {\n\t\t\tname: { label: 'app:browser', color: 'blue' },\n\t\t\troot: resolveWorkspacePath('.'),\n\t\t\tdir: resolveWorkspacePath('.'),\n\t\t\tinclude: ['tests/app/browser/**/*.test.ts'],\n\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n\t\t\tbrowser: {\n\t\t\t\tenabled: true,\n\t\t\t\tprovider: playwright(),\n\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t},\n\t\t\tfileParallelism: false,\n\t\t},\n\t}\n}\n\nexport function appBrowser(...options: never[]): UserConfig {\n\tif (options.length > 0) throw new Error('Browser configuration overrides are not permitted')\n\treturn applicationBrowser(false)\n}\n{{showcaseFactory}}";
|
|
956
977
|
server: "export const appServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('app/server/main.ts'),\n\t\t\t\t\tformats: ['cjs'],\n\t\t\t\t\tfileName: () => 'main.cjs',\n\t\t\t\t},\n\t\t\t\toutDir: resolveWorkspacePath('dist/app/server'),\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: { external: (id: string) => id.startsWith('node:') },\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'app:server', color: 'green' },\n\t\t\t\tinclude: ['tests/app/server/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
957
978
|
}>;
|
|
958
979
|
policy: "export const policy = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\ttest: {\n\t\t\t\tname: { label: 'policy', color: 'white' },\n\t\t\t\tinclude: ['tests/policy.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
@@ -1171,6 +1192,21 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1171
1192
|
*/
|
|
1172
1193
|
export declare const ENVIRONMENTS: readonly Environment[];
|
|
1173
1194
|
|
|
1195
|
+
/**
|
|
1196
|
+
* The vendored paths a target receives with its executable bit set, frozen.
|
|
1197
|
+
*
|
|
1198
|
+
* @remarks
|
|
1199
|
+
* Declared rather than read from the staging host's filesystem, because that
|
|
1200
|
+
* reading is not portable: Windows carries no executable bit, so a host staged
|
|
1201
|
+
* there reports every file non-executable and every target receives hooks it
|
|
1202
|
+
* cannot run. Declaring the set here makes one checkout stage one manifest on
|
|
1203
|
+
* every host.
|
|
1204
|
+
*
|
|
1205
|
+
* Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
|
|
1206
|
+
* that must run when a target invokes it belongs here the moment it is vendored.
|
|
1207
|
+
*/
|
|
1208
|
+
export declare const EXECUTABLE_PATHS: readonly string[];
|
|
1209
|
+
|
|
1174
1210
|
/**
|
|
1175
1211
|
* The development extra name syntax: any valid npm package name.
|
|
1176
1212
|
*
|
|
@@ -1220,21 +1256,45 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1220
1256
|
* the audit showed is the one thing the destructive verb must never do. A
|
|
1221
1257
|
* missing destination has no bytes to record. An aligned one may have gone
|
|
1222
1258
|
* uncompared, which is what a birth-owned path always does, so it records
|
|
1223
|
-
* bytes only where they were actually read.
|
|
1259
|
+
* bytes only where they were actually read. Every planned finding carries its
|
|
1260
|
+
* artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
|
|
1261
|
+
* path from the finding itself. A foreign finding has no planned artifact and
|
|
1262
|
+
* therefore no ownership.
|
|
1263
|
+
*
|
|
1264
|
+
* Which combinations of `ownership`, `drift`, and `observed` a real audit
|
|
1265
|
+
* produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
|
|
1266
|
+
* never compared and is always aligned, a presence-owned path compares existence
|
|
1267
|
+
* only, and bytes are recorded only where they were read. This shape therefore
|
|
1268
|
+
* admits a combination that law never produces — a birth-owned path reported
|
|
1269
|
+
* stale is the plainest one — and it admits it deliberately: restating the
|
|
1270
|
+
* comparison's case analysis here would be a second copy of it, able to disagree
|
|
1271
|
+
* with the one that decides. What closes the gap is the writer: `repair` and
|
|
1272
|
+
* `remove` re-derive every verdict themselves and refuse a caller's audit that
|
|
1273
|
+
* does not match, so a verdict the comparison could not have reached is refused
|
|
1274
|
+
* rather than acted on.
|
|
1224
1275
|
*/
|
|
1225
1276
|
export declare type Finding = {
|
|
1226
1277
|
readonly path: string;
|
|
1227
1278
|
readonly group: Group;
|
|
1228
|
-
readonly
|
|
1279
|
+
readonly ownership: Ownership;
|
|
1280
|
+
readonly drift: 'stale';
|
|
1229
1281
|
readonly observed: string;
|
|
1230
1282
|
} | {
|
|
1231
1283
|
readonly path: string;
|
|
1232
1284
|
readonly group: Group;
|
|
1285
|
+
readonly ownership?: never;
|
|
1286
|
+
readonly drift: 'foreign';
|
|
1287
|
+
readonly observed: string;
|
|
1288
|
+
} | {
|
|
1289
|
+
readonly path: string;
|
|
1290
|
+
readonly group: Group;
|
|
1291
|
+
readonly ownership: Ownership;
|
|
1233
1292
|
readonly drift: 'missing';
|
|
1234
1293
|
readonly observed?: never;
|
|
1235
1294
|
} | {
|
|
1236
1295
|
readonly path: string;
|
|
1237
1296
|
readonly group: Group;
|
|
1297
|
+
readonly ownership: Ownership;
|
|
1238
1298
|
readonly drift: 'aligned';
|
|
1239
1299
|
readonly observed?: string;
|
|
1240
1300
|
};
|
|
@@ -1381,7 +1441,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1381
1441
|
/** The installed-package proof whose presence makes a workspace `integration`. */
|
|
1382
1442
|
export declare const INTEGRATION_TEST_PATH = "tests/integration.test.ts";
|
|
1383
1443
|
|
|
1384
|
-
/** Visible characters a
|
|
1444
|
+
/** Visible characters a target-relative path and a Markdown path cell both forbid. */
|
|
1385
1445
|
export declare const INVALID_PATH_CHARACTER_PATTERN: RegExp;
|
|
1386
1446
|
|
|
1387
1447
|
/**
|
|
@@ -1408,7 +1468,8 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1408
1468
|
*
|
|
1409
1469
|
* @remarks
|
|
1410
1470
|
* An audit reaches the writer and the destructive verb, so it is guarded as
|
|
1411
|
-
* strictly as the plan beside it.
|
|
1471
|
+
* strictly as the plan beside it. Findings use the sum of the two producer
|
|
1472
|
+
* bounds: one per planned artifact, then one per unplanned snapshot path.
|
|
1412
1473
|
*/
|
|
1413
1474
|
export declare const isAudit: Guard<Audit>;
|
|
1414
1475
|
|
|
@@ -1552,7 +1613,13 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1552
1613
|
* @remarks
|
|
1553
1614
|
* `observed` is required exactly where the mutation it precedes is held to it,
|
|
1554
1615
|
* absent where the destination had no bytes to record, and optional where the
|
|
1555
|
-
* comparison may not have been made.
|
|
1616
|
+
* comparison may not have been made. Planned findings require `ownership`;
|
|
1617
|
+
* foreign findings forbid it because no artifact was planned for their path.
|
|
1618
|
+
*
|
|
1619
|
+
* That is the whole claim. This guard proves the shape a reader may destructure
|
|
1620
|
+
* and nothing about whether the verdict is one an audit could have reached: the
|
|
1621
|
+
* correlation between `ownership`, `drift`, and `observed` belongs to
|
|
1622
|
+
* {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
|
|
1556
1623
|
*/
|
|
1557
1624
|
export declare const isFinding: Guard<Finding>;
|
|
1558
1625
|
|
|
@@ -1617,19 +1684,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1617
1684
|
export declare const isOverride: Guard<Override>;
|
|
1618
1685
|
|
|
1619
1686
|
/**
|
|
1620
|
-
* Narrow a value to a
|
|
1687
|
+
* Narrow a value to a logical target-relative path.
|
|
1621
1688
|
*
|
|
1622
1689
|
* @param value - The candidate path.
|
|
1623
1690
|
* @returns `true` for a bounded relative path with no traversal, empty segment,
|
|
1624
|
-
* control character, or
|
|
1691
|
+
* control character, or reserved syntax character.
|
|
1625
1692
|
*
|
|
1626
1693
|
* @remarks
|
|
1627
1694
|
* Every path this package reads or writes passes here, so one law covers a
|
|
1628
1695
|
* planned artifact, an override target, an audit finding, a guide mirror, and a
|
|
1629
1696
|
* snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
|
|
1630
1697
|
* what stops a caller-supplied path from naming a destination outside the
|
|
1631
|
-
* target
|
|
1632
|
-
*
|
|
1698
|
+
* target. Host-location validation is a separate server boundary: this guard
|
|
1699
|
+
* does not reject a device spelling, a trailing dot or space, or a segment that
|
|
1700
|
+
* exceeds a host filesystem's byte ceiling.
|
|
1633
1701
|
*
|
|
1634
1702
|
* @example
|
|
1635
1703
|
* ```ts
|
|
@@ -1763,6 +1831,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1763
1831
|
*/
|
|
1764
1832
|
export declare function manifestToName(manifest: string): string | undefined;
|
|
1765
1833
|
|
|
1834
|
+
/**
|
|
1835
|
+
* Test whether {@link inferDrift} could have produced a finding for an ownership.
|
|
1836
|
+
*
|
|
1837
|
+
* @param ownership - What scaffold claims at the planned path.
|
|
1838
|
+
* @param finding - The audit verdict to test.
|
|
1839
|
+
* @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
|
|
1840
|
+
*
|
|
1841
|
+
* @remarks
|
|
1842
|
+
* This predicate keeps the comparison law beside the reachability law it
|
|
1843
|
+
* restates. A mutation uses it so a refusal can distinguish an impossible
|
|
1844
|
+
* verdict from a target that genuinely moved after its audit.
|
|
1845
|
+
*/
|
|
1846
|
+
export declare function matchesDriftReachability(ownership: Ownership, finding: Finding): boolean;
|
|
1847
|
+
|
|
1766
1848
|
/**
|
|
1767
1849
|
* Test whether a declared engines floor is at or above the supported minimum.
|
|
1768
1850
|
*
|
|
@@ -1861,6 +1943,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1861
1943
|
/** Maximum length of the hexadecimal string carrying one artifact's bytes. */
|
|
1862
1944
|
export declare const MAX_ARTIFACT_HEX_LENGTH: number;
|
|
1863
1945
|
|
|
1946
|
+
/** Maximum findings one audit can produce from a bounded plan and snapshot. */
|
|
1947
|
+
export declare const MAX_AUDIT_FINDINGS: number;
|
|
1948
|
+
|
|
1864
1949
|
/** Maximum items accepted in one public collection. */
|
|
1865
1950
|
export declare const MAX_COLLECTION_ITEMS = 1000;
|
|
1866
1951
|
|
|
@@ -2024,9 +2109,10 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2024
2109
|
*
|
|
2025
2110
|
* @remarks
|
|
2026
2111
|
* `content` replaces the rendered artifact at `path` and never partially
|
|
2027
|
-
* merges it.
|
|
2028
|
-
*
|
|
2029
|
-
*
|
|
2112
|
+
* merges it. Legality is measured against every artifact the blueprint drafts,
|
|
2113
|
+
* before a compile narrows the returned groups. An override that matches none
|
|
2114
|
+
* of those artifacts, that targets a host-origin artifact, or that targets the
|
|
2115
|
+
* manifest is a blocking question rather than a silent no-op.
|
|
2030
2116
|
*/
|
|
2031
2117
|
export declare interface Override {
|
|
2032
2118
|
readonly path: string;
|
|
@@ -2051,10 +2137,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2051
2137
|
*
|
|
2052
2138
|
* @example
|
|
2053
2139
|
* ```ts
|
|
2054
|
-
* import
|
|
2055
|
-
* import { overridesToQuestions } from '@orkestrel/scaffold'
|
|
2140
|
+
* import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
|
|
2056
2141
|
*
|
|
2057
|
-
*
|
|
2142
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
2058
2143
|
*
|
|
2059
2144
|
* overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
|
|
2060
2145
|
* ```
|
|
@@ -2224,12 +2309,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2224
2309
|
*
|
|
2225
2310
|
* @example
|
|
2226
2311
|
* ```ts
|
|
2227
|
-
* import
|
|
2228
|
-
* import { planToFindings } from '@orkestrel/scaffold'
|
|
2312
|
+
* import { createBlueprint, createCompiler, planToFindings } from '@orkestrel/scaffold'
|
|
2229
2313
|
*
|
|
2230
|
-
*
|
|
2314
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2231
2315
|
*
|
|
2232
|
-
* planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2316
|
+
* plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2233
2317
|
* ```
|
|
2234
2318
|
*/
|
|
2235
2319
|
export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
|
|
@@ -2255,12 +2339,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2255
2339
|
*
|
|
2256
2340
|
* @example
|
|
2257
2341
|
* ```ts
|
|
2258
|
-
* import
|
|
2259
|
-
* import { planToHash } from '@orkestrel/scaffold'
|
|
2342
|
+
* import { createBlueprint, createCompiler, planToHash } from '@orkestrel/scaffold'
|
|
2260
2343
|
*
|
|
2261
|
-
*
|
|
2344
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2262
2345
|
*
|
|
2263
|
-
* planToHash(plan)?.length // 16
|
|
2346
|
+
* plan === undefined ? undefined : planToHash(plan)?.length // 16
|
|
2264
2347
|
* ```
|
|
2265
2348
|
*/
|
|
2266
2349
|
export declare function planToHash(plan: Plan): string | undefined;
|
|
@@ -2331,16 +2414,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2331
2414
|
* The one error this package throws, carrying the coded reason it was raised.
|
|
2332
2415
|
*
|
|
2333
2416
|
* @remarks
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
* `
|
|
2338
|
-
*
|
|
2339
|
-
*
|
|
2340
|
-
*
|
|
2341
|
-
*
|
|
2342
|
-
*
|
|
2343
|
-
*
|
|
2417
|
+
* Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
|
|
2418
|
+
* any call made after teardown, `TARGET` for a destination that is not what the
|
|
2419
|
+
* caller's observation said it was, `WRITE` for a mutation that could not be
|
|
2420
|
+
* completed, `FETCH` for an upstream read that produced no answer the caller can
|
|
2421
|
+
* be given, and `BLOCKED` for a refused blueprint.
|
|
2422
|
+
*
|
|
2423
|
+
* `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
|
|
2424
|
+
* — this blueprint will not be built — and the questions say which. The compiler
|
|
2425
|
+
* answers its refusal rather than throwing it: the gate fails closed, returns the
|
|
2426
|
+
* questions that closed it, and records `BLOCKED` on its stage, so a caller reads
|
|
2427
|
+
* that refusal from the value it asked for. A verb that creates a workspace
|
|
2428
|
+
* throws it, because it chose the shape and has nothing to hand back. A blocking
|
|
2429
|
+
* question closed the gate; a non-blocking one is a shape this package can
|
|
2430
|
+
* describe and declines to create.
|
|
2344
2431
|
*
|
|
2345
2432
|
* `context` carries whatever the raising site can say about the failure. It is
|
|
2346
2433
|
* `unknown` because nothing narrows it usefully at the catch site; read it for
|