@orkestrel/scaffold 0.0.25 → 0.0.27
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 +216 -65
- package/dist/host/claude/agents/orkestrel.md +80 -48
- package/dist/host/claude/agents/researcher.md +1 -0
- package/dist/host/claude/agents/scout.md +1 -0
- package/dist/host/claude/rules/documentation.md +3 -1
- package/dist/host/claude/rules/quality.md +1 -0
- package/dist/host/claude/rules/tests.md +5 -0
- package/dist/host/guides/guide.md +211 -100
- package/dist/host/guides/scaffold.md +206 -10
- package/dist/host/manifest.json +5 -5
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +30 -7
- package/dist/src/core/index.cjs +300 -157
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +227 -98
- package/dist/src/core/index.d.ts +227 -98
- package/dist/src/core/index.js +297 -158
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +206 -70
- 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 +208 -73
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -8
package/dist/src/core/index.d.ts
CHANGED
|
@@ -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
|
* ```
|
|
@@ -600,19 +609,61 @@ export declare const CATALOG_AGENT_PATH = ".claude/agents/orkestrel.md";
|
|
|
600
609
|
* still a row: dropping it would hide a package the organization publishes
|
|
601
610
|
* behind one failed request, and inventing a version would state something
|
|
602
611
|
* upstream never said.
|
|
612
|
+
*
|
|
613
|
+
* `dependencies` are the RUNTIME edges the published version declares, which is
|
|
614
|
+
* what a publish order is computed over: a runtime bump obliges every dependent
|
|
615
|
+
* to re-pin and republish, while a development bump obliges nothing beyond the
|
|
616
|
+
* repository that declares it. No layer is recorded here, because a layer is a
|
|
617
|
+
* deterministic function of these edges across the whole catalog — a stored one
|
|
618
|
+
* could only disagree with the rows it was derived from. Read it through
|
|
619
|
+
* {@link catalogToLayers}.
|
|
603
620
|
*/
|
|
604
621
|
export declare type CatalogEntry = {
|
|
605
622
|
readonly name: string;
|
|
606
623
|
readonly lookup: 'found';
|
|
607
624
|
readonly version: string;
|
|
625
|
+
readonly dependencies: readonly Dependency[];
|
|
608
626
|
readonly note?: never;
|
|
609
627
|
} | {
|
|
610
628
|
readonly name: string;
|
|
611
629
|
readonly lookup: 'missing' | 'failed';
|
|
612
630
|
readonly note: string;
|
|
613
631
|
readonly version?: never;
|
|
632
|
+
readonly dependencies?: never;
|
|
614
633
|
};
|
|
615
634
|
|
|
635
|
+
/**
|
|
636
|
+
* Project a catalog into the layers it publishes in.
|
|
637
|
+
*
|
|
638
|
+
* @param entries - The catalog rows to order.
|
|
639
|
+
* @returns One layer per round, each holding the names publishable together,
|
|
640
|
+
* sorted within the layer; a name whose edges never resolve is omitted.
|
|
641
|
+
*
|
|
642
|
+
* @remarks
|
|
643
|
+
* A layer is a deterministic function of the catalog's own edges, so it is
|
|
644
|
+
* computed here rather than stored on a row that could disagree with them.
|
|
645
|
+
* Only RUNTIME edges between catalogued packages count: a development
|
|
646
|
+
* dependency reaches no consumer, so it constrains nothing about publish order,
|
|
647
|
+
* and an edge leaving the fleet is a package this catalog does not publish.
|
|
648
|
+
*
|
|
649
|
+
* The order matters because these packages are `0.0.x`, where a caret pins one
|
|
650
|
+
* exact release. Publishing a dependent before its dependency leaves the
|
|
651
|
+
* dependent pinned to the older release, and two ranges that disagree install
|
|
652
|
+
* two copies of one package that the compiler reads as two distinct types.
|
|
653
|
+
*
|
|
654
|
+
* A cycle cannot be published in rounds, so its members are omitted rather than
|
|
655
|
+
* placed in an order that would be wrong. An absent name is the report: compare
|
|
656
|
+
* the returned names against the catalog to find one.
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```ts
|
|
660
|
+
* import { catalogToLayers } from '@orkestrel/scaffold'
|
|
661
|
+
*
|
|
662
|
+
* catalogToLayers(entries)[0] // the names that depend on nothing in the fleet
|
|
663
|
+
* ```
|
|
664
|
+
*/
|
|
665
|
+
export declare function catalogToLayers(entries: readonly CatalogEntry[]): ReadonlyArray<readonly string[]>;
|
|
666
|
+
|
|
616
667
|
/**
|
|
617
668
|
* Snapshot an untrusted value into exact JSON data the caller owns.
|
|
618
669
|
*
|
|
@@ -759,12 +810,19 @@ export declare class Compiler implements CompilerInterface {
|
|
|
759
810
|
* carries no plan, then `compile` with the whole outcome either way, so an
|
|
760
811
|
* observer reads every compile from one event and the refusals from the other.
|
|
761
812
|
*
|
|
813
|
+
* A plan says the blueprint can be built. It does not say the blueprint should
|
|
814
|
+
* be created, and the questions beside it are what this compiler could not
|
|
815
|
+
* settle. A caller creating a fresh workspace answers them first and writes
|
|
816
|
+
* nothing while any remains, which is the rule the `new` verb applies; a caller
|
|
817
|
+
* describing or repairing an existing target carries them through instead.
|
|
818
|
+
* Nothing downstream repeats that check, because only the caller knows which of
|
|
819
|
+
* the two it is.
|
|
820
|
+
*
|
|
762
821
|
* @example
|
|
763
822
|
* ```ts
|
|
764
|
-
* import
|
|
765
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
823
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
766
824
|
*
|
|
767
|
-
*
|
|
825
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
768
826
|
*
|
|
769
827
|
* createCompiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
|
|
770
828
|
* ```
|
|
@@ -785,14 +843,19 @@ export declare class Compiler implements CompilerInterface {
|
|
|
785
843
|
* reports no findings and carries the questions instead. Emits `block` in that
|
|
786
844
|
* case, then `audit` with the verdict either way.
|
|
787
845
|
*
|
|
846
|
+
* Ownership decides each verdict, not absence. A birth-owned path is never
|
|
847
|
+
* compared and reads `aligned` against a target holding nothing, while a
|
|
848
|
+
* content-owned path with no bytes to read is `missing`. An empty snapshot
|
|
849
|
+
* therefore produces both verdicts rather than one.
|
|
850
|
+
*
|
|
788
851
|
* @example
|
|
789
852
|
* ```ts
|
|
790
|
-
* import
|
|
791
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
853
|
+
* import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
|
|
792
854
|
*
|
|
793
|
-
*
|
|
855
|
+
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
794
856
|
*
|
|
795
|
-
* createCompiler().audit(blueprint, {}).findings
|
|
857
|
+
* createCompiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
|
|
858
|
+
* createCompiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
|
|
796
859
|
* ```
|
|
797
860
|
*/
|
|
798
861
|
audit(blueprint: Blueprint, current: Snapshot, groups?: readonly Group[]): Audit;
|
|
@@ -941,7 +1004,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
941
1004
|
export declare const CONFIG_TEMPLATES: Readonly<{
|
|
942
1005
|
root: Readonly<{
|
|
943
1006
|
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 {
|
|
1007
|
+
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
1008
|
}>;
|
|
946
1009
|
factories: Readonly<{
|
|
947
1010
|
src: Readonly<{
|
|
@@ -952,7 +1015,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
952
1015
|
}>;
|
|
953
1016
|
app: Readonly<{
|
|
954
1017
|
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
|
|
1018
|
+
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
1019
|
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
1020
|
}>;
|
|
958
1021
|
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 +1234,21 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1171
1234
|
*/
|
|
1172
1235
|
export declare const ENVIRONMENTS: readonly Environment[];
|
|
1173
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* The vendored paths a target receives with its executable bit set, frozen.
|
|
1239
|
+
*
|
|
1240
|
+
* @remarks
|
|
1241
|
+
* Declared rather than read from the staging host's filesystem, because that
|
|
1242
|
+
* reading is not portable: Windows carries no executable bit, so a host staged
|
|
1243
|
+
* there reports every file non-executable and every target receives hooks it
|
|
1244
|
+
* cannot run. Declaring the set here makes one checkout stage one manifest on
|
|
1245
|
+
* every host.
|
|
1246
|
+
*
|
|
1247
|
+
* Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
|
|
1248
|
+
* that must run when a target invokes it belongs here the moment it is vendored.
|
|
1249
|
+
*/
|
|
1250
|
+
export declare const EXECUTABLE_PATHS: readonly string[];
|
|
1251
|
+
|
|
1174
1252
|
/**
|
|
1175
1253
|
* The development extra name syntax: any valid npm package name.
|
|
1176
1254
|
*
|
|
@@ -1220,21 +1298,45 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1220
1298
|
* the audit showed is the one thing the destructive verb must never do. A
|
|
1221
1299
|
* missing destination has no bytes to record. An aligned one may have gone
|
|
1222
1300
|
* uncompared, which is what a birth-owned path always does, so it records
|
|
1223
|
-
* bytes only where they were actually read.
|
|
1301
|
+
* bytes only where they were actually read. Every planned finding carries its
|
|
1302
|
+
* artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
|
|
1303
|
+
* path from the finding itself. A foreign finding has no planned artifact and
|
|
1304
|
+
* therefore no ownership.
|
|
1305
|
+
*
|
|
1306
|
+
* Which combinations of `ownership`, `drift`, and `observed` a real audit
|
|
1307
|
+
* produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
|
|
1308
|
+
* never compared and is always aligned, a presence-owned path compares existence
|
|
1309
|
+
* only, and bytes are recorded only where they were read. This shape therefore
|
|
1310
|
+
* admits a combination that law never produces — a birth-owned path reported
|
|
1311
|
+
* stale is the plainest one — and it admits it deliberately: restating the
|
|
1312
|
+
* comparison's case analysis here would be a second copy of it, able to disagree
|
|
1313
|
+
* with the one that decides. What closes the gap is the writer: `repair` and
|
|
1314
|
+
* `remove` re-derive every verdict themselves and refuse a caller's audit that
|
|
1315
|
+
* does not match, so a verdict the comparison could not have reached is refused
|
|
1316
|
+
* rather than acted on.
|
|
1224
1317
|
*/
|
|
1225
1318
|
export declare type Finding = {
|
|
1226
1319
|
readonly path: string;
|
|
1227
1320
|
readonly group: Group;
|
|
1228
|
-
readonly
|
|
1321
|
+
readonly ownership: Ownership;
|
|
1322
|
+
readonly drift: 'stale';
|
|
1229
1323
|
readonly observed: string;
|
|
1230
1324
|
} | {
|
|
1231
1325
|
readonly path: string;
|
|
1232
1326
|
readonly group: Group;
|
|
1327
|
+
readonly ownership?: never;
|
|
1328
|
+
readonly drift: 'foreign';
|
|
1329
|
+
readonly observed: string;
|
|
1330
|
+
} | {
|
|
1331
|
+
readonly path: string;
|
|
1332
|
+
readonly group: Group;
|
|
1333
|
+
readonly ownership: Ownership;
|
|
1233
1334
|
readonly drift: 'missing';
|
|
1234
1335
|
readonly observed?: never;
|
|
1235
1336
|
} | {
|
|
1236
1337
|
readonly path: string;
|
|
1237
1338
|
readonly group: Group;
|
|
1339
|
+
readonly ownership: Ownership;
|
|
1238
1340
|
readonly drift: 'aligned';
|
|
1239
1341
|
readonly observed?: string;
|
|
1240
1342
|
};
|
|
@@ -1381,7 +1483,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1381
1483
|
/** The installed-package proof whose presence makes a workspace `integration`. */
|
|
1382
1484
|
export declare const INTEGRATION_TEST_PATH = "tests/integration.test.ts";
|
|
1383
1485
|
|
|
1384
|
-
/** Visible characters a
|
|
1486
|
+
/** Visible characters a target-relative path and a Markdown path cell both forbid. */
|
|
1385
1487
|
export declare const INVALID_PATH_CHARACTER_PATTERN: RegExp;
|
|
1386
1488
|
|
|
1387
1489
|
/**
|
|
@@ -1408,7 +1510,8 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1408
1510
|
*
|
|
1409
1511
|
* @remarks
|
|
1410
1512
|
* An audit reaches the writer and the destructive verb, so it is guarded as
|
|
1411
|
-
* strictly as the plan beside it.
|
|
1513
|
+
* strictly as the plan beside it. Findings use the sum of the two producer
|
|
1514
|
+
* bounds: one per planned artifact, then one per unplanned snapshot path.
|
|
1412
1515
|
*/
|
|
1413
1516
|
export declare const isAudit: Guard<Audit>;
|
|
1414
1517
|
|
|
@@ -1552,7 +1655,13 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1552
1655
|
* @remarks
|
|
1553
1656
|
* `observed` is required exactly where the mutation it precedes is held to it,
|
|
1554
1657
|
* absent where the destination had no bytes to record, and optional where the
|
|
1555
|
-
* comparison may not have been made.
|
|
1658
|
+
* comparison may not have been made. Planned findings require `ownership`;
|
|
1659
|
+
* foreign findings forbid it because no artifact was planned for their path.
|
|
1660
|
+
*
|
|
1661
|
+
* That is the whole claim. This guard proves the shape a reader may destructure
|
|
1662
|
+
* and nothing about whether the verdict is one an audit could have reached: the
|
|
1663
|
+
* correlation between `ownership`, `drift`, and `observed` belongs to
|
|
1664
|
+
* {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
|
|
1556
1665
|
*/
|
|
1557
1666
|
export declare const isFinding: Guard<Finding>;
|
|
1558
1667
|
|
|
@@ -1617,19 +1726,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1617
1726
|
export declare const isOverride: Guard<Override>;
|
|
1618
1727
|
|
|
1619
1728
|
/**
|
|
1620
|
-
* Narrow a value to a
|
|
1729
|
+
* Narrow a value to a logical target-relative path.
|
|
1621
1730
|
*
|
|
1622
1731
|
* @param value - The candidate path.
|
|
1623
1732
|
* @returns `true` for a bounded relative path with no traversal, empty segment,
|
|
1624
|
-
* control character, or
|
|
1733
|
+
* control character, or reserved syntax character.
|
|
1625
1734
|
*
|
|
1626
1735
|
* @remarks
|
|
1627
1736
|
* Every path this package reads or writes passes here, so one law covers a
|
|
1628
1737
|
* planned artifact, an override target, an audit finding, a guide mirror, and a
|
|
1629
1738
|
* snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
|
|
1630
1739
|
* what stops a caller-supplied path from naming a destination outside the
|
|
1631
|
-
* target
|
|
1632
|
-
*
|
|
1740
|
+
* target. Host-location validation is a separate server boundary: this guard
|
|
1741
|
+
* does not reject a device spelling, a trailing dot or space, or a segment that
|
|
1742
|
+
* exceeds a host filesystem's byte ceiling.
|
|
1633
1743
|
*
|
|
1634
1744
|
* @example
|
|
1635
1745
|
* ```ts
|
|
@@ -1763,6 +1873,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1763
1873
|
*/
|
|
1764
1874
|
export declare function manifestToName(manifest: string): string | undefined;
|
|
1765
1875
|
|
|
1876
|
+
/**
|
|
1877
|
+
* Test whether {@link inferDrift} could have produced a finding for an ownership.
|
|
1878
|
+
*
|
|
1879
|
+
* @param ownership - What scaffold claims at the planned path.
|
|
1880
|
+
* @param finding - The audit verdict to test.
|
|
1881
|
+
* @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
|
|
1882
|
+
*
|
|
1883
|
+
* @remarks
|
|
1884
|
+
* This predicate keeps the comparison law beside the reachability law it
|
|
1885
|
+
* restates. A mutation uses it so a refusal can distinguish an impossible
|
|
1886
|
+
* verdict from a target that genuinely moved after its audit.
|
|
1887
|
+
*/
|
|
1888
|
+
export declare function matchesDriftReachability(ownership: Ownership, finding: Finding): boolean;
|
|
1889
|
+
|
|
1766
1890
|
/**
|
|
1767
1891
|
* Test whether a declared engines floor is at or above the supported minimum.
|
|
1768
1892
|
*
|
|
@@ -1861,6 +1985,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1861
1985
|
/** Maximum length of the hexadecimal string carrying one artifact's bytes. */
|
|
1862
1986
|
export declare const MAX_ARTIFACT_HEX_LENGTH: number;
|
|
1863
1987
|
|
|
1988
|
+
/** Maximum findings one audit can produce from a bounded plan and snapshot. */
|
|
1989
|
+
export declare const MAX_AUDIT_FINDINGS: number;
|
|
1990
|
+
|
|
1864
1991
|
/** Maximum items accepted in one public collection. */
|
|
1865
1992
|
export declare const MAX_COLLECTION_ITEMS = 1000;
|
|
1866
1993
|
|
|
@@ -2024,9 +2151,10 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2024
2151
|
*
|
|
2025
2152
|
* @remarks
|
|
2026
2153
|
* `content` replaces the rendered artifact at `path` and never partially
|
|
2027
|
-
* merges it.
|
|
2028
|
-
*
|
|
2029
|
-
*
|
|
2154
|
+
* merges it. Legality is measured against every artifact the blueprint drafts,
|
|
2155
|
+
* before a compile narrows the returned groups. An override that matches none
|
|
2156
|
+
* of those artifacts, that targets a host-origin artifact, or that targets the
|
|
2157
|
+
* manifest is a blocking question rather than a silent no-op.
|
|
2030
2158
|
*/
|
|
2031
2159
|
export declare interface Override {
|
|
2032
2160
|
readonly path: string;
|
|
@@ -2051,10 +2179,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2051
2179
|
*
|
|
2052
2180
|
* @example
|
|
2053
2181
|
* ```ts
|
|
2054
|
-
* import
|
|
2055
|
-
* import { overridesToQuestions } from '@orkestrel/scaffold'
|
|
2182
|
+
* import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
|
|
2056
2183
|
*
|
|
2057
|
-
*
|
|
2184
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
2058
2185
|
*
|
|
2059
2186
|
* overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
|
|
2060
2187
|
* ```
|
|
@@ -2224,12 +2351,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2224
2351
|
*
|
|
2225
2352
|
* @example
|
|
2226
2353
|
* ```ts
|
|
2227
|
-
* import
|
|
2228
|
-
* import { planToFindings } from '@orkestrel/scaffold'
|
|
2354
|
+
* import { createBlueprint, createCompiler, planToFindings } from '@orkestrel/scaffold'
|
|
2229
2355
|
*
|
|
2230
|
-
*
|
|
2356
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2231
2357
|
*
|
|
2232
|
-
* planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2358
|
+
* plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2233
2359
|
* ```
|
|
2234
2360
|
*/
|
|
2235
2361
|
export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
|
|
@@ -2255,12 +2381,11 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2255
2381
|
*
|
|
2256
2382
|
* @example
|
|
2257
2383
|
* ```ts
|
|
2258
|
-
* import
|
|
2259
|
-
* import { planToHash } from '@orkestrel/scaffold'
|
|
2384
|
+
* import { createBlueprint, createCompiler, planToHash } from '@orkestrel/scaffold'
|
|
2260
2385
|
*
|
|
2261
|
-
*
|
|
2386
|
+
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2262
2387
|
*
|
|
2263
|
-
* planToHash(plan)?.length // 16
|
|
2388
|
+
* plan === undefined ? undefined : planToHash(plan)?.length // 16
|
|
2264
2389
|
* ```
|
|
2265
2390
|
*/
|
|
2266
2391
|
export declare function planToHash(plan: Plan): string | undefined;
|
|
@@ -2331,16 +2456,20 @@ export declare class Compiler implements CompilerInterface {
|
|
|
2331
2456
|
* The one error this package throws, carrying the coded reason it was raised.
|
|
2332
2457
|
*
|
|
2333
2458
|
* @remarks
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
* `
|
|
2338
|
-
*
|
|
2339
|
-
*
|
|
2340
|
-
*
|
|
2341
|
-
*
|
|
2342
|
-
*
|
|
2343
|
-
*
|
|
2459
|
+
* Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
|
|
2460
|
+
* any call made after teardown, `TARGET` for a destination that is not what the
|
|
2461
|
+
* caller's observation said it was, `WRITE` for a mutation that could not be
|
|
2462
|
+
* completed, `FETCH` for an upstream read that produced no answer the caller can
|
|
2463
|
+
* be given, and `BLOCKED` for a refused blueprint.
|
|
2464
|
+
*
|
|
2465
|
+
* `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
|
|
2466
|
+
* — this blueprint will not be built — and the questions say which. The compiler
|
|
2467
|
+
* answers its refusal rather than throwing it: the gate fails closed, returns the
|
|
2468
|
+
* questions that closed it, and records `BLOCKED` on its stage, so a caller reads
|
|
2469
|
+
* that refusal from the value it asked for. A verb that creates a workspace
|
|
2470
|
+
* throws it, because it chose the shape and has nothing to hand back. A blocking
|
|
2471
|
+
* question closed the gate; a non-blocking one is a shape this package can
|
|
2472
|
+
* describe and declines to create.
|
|
2344
2473
|
*
|
|
2345
2474
|
* `context` carries whatever the raising site can say about the failure. It is
|
|
2346
2475
|
* `unknown` because nothing narrows it usefully at the catch site; read it for
|