@orkestrel/scaffold 0.0.44 → 0.0.45
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 +10 -10
- package/dist/bin/main.js +31 -31
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +7 -2
- package/dist/host/agents/orchestration.md +232 -56
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +7 -7
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +11 -9
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +3 -3
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +3 -3
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +1 -1
- package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +2 -2
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +2 -2
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +8 -8
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +16 -14
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +3 -3
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +14 -14
- package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-human-journey/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-human-journey/references/captures.md +3 -3
- package/dist/host/agents/skills/orkestrel-human-journey/references/layer.md +3 -3
- package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +3 -3
- package/dist/host/claude/agents/analyst.md +2 -2
- package/dist/host/claude/agents/checker.md +2 -2
- package/dist/host/claude/agents/codex.md +5 -5
- package/dist/host/claude/agents/orkestrel.md +9 -8
- package/dist/host/claude/agents/planner.md +1 -1
- package/dist/host/claude/agents/researcher.md +2 -2
- package/dist/host/claude/agents/reviewer.md +1 -1
- package/dist/host/claude/agents/scout.md +2 -2
- package/dist/host/claude/agents/sol.md +3 -3
- package/dist/host/claude/agents/verifier.md +8 -0
- package/dist/host/claude/rules/application.md +7 -7
- package/dist/host/claude/rules/architecture.md +6 -6
- package/dist/host/claude/rules/documentation.md +1 -0
- package/dist/host/claude/rules/patterns.md +3 -3
- package/dist/host/claude/rules/quality.md +3 -3
- package/dist/host/claude/rules/tests.md +9 -2
- package/dist/host/claude/rules/workspace.md +7 -7
- package/dist/host/claude/rules/writing.md +12 -2
- package/dist/host/codex/agents/planner.toml +1 -1
- package/dist/host/configs/helpers.ts +21 -1
- package/dist/host/cursor/rules/orchestration.mdc +1 -1
- package/dist/host/dotfiles/gitignore +4 -1
- package/dist/host/guides/scaffold.md +134 -119
- 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 +40 -1
- package/dist/host/tests/policy.test.ts +2 -2
- package/dist/host/tests/setupPolicy.ts +8 -5
- package/dist/src/core/index.cjs +143 -149
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1624 -1643
- package/dist/src/core/index.d.ts +1624 -1643
- package/dist/src/core/index.js +144 -149
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +82 -97
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +1844 -1871
- package/dist/src/server/index.d.ts +1844 -1871
- package/dist/src/server/index.js +83 -96
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -16,7 +16,7 @@ export declare const APP_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
|
16
16
|
* @remarks
|
|
17
17
|
* An application environment declares no exports, so it carries a runtime
|
|
18
18
|
* entry instead of a subpath and formats. Core carries none because it is
|
|
19
|
-
* shared logic the other
|
|
19
|
+
* shared logic the other environments import rather than a host that runs.
|
|
20
20
|
*/
|
|
21
21
|
export declare const APP_MATRIX: Readonly<Record<Environment, AppDefinition>>;
|
|
22
22
|
|
|
@@ -159,7 +159,7 @@ export declare function artifactsToQuestions(artifacts: readonly Artifact[]): re
|
|
|
159
159
|
* destination has no bytes to record, and every other verdict records the bytes
|
|
160
160
|
* it was given, which is the precondition the mutation that follows is held to.
|
|
161
161
|
* Ownership is copied rather than inferred from drift because aligned findings
|
|
162
|
-
* span
|
|
162
|
+
* span every ownership tier.
|
|
163
163
|
*
|
|
164
164
|
* `foreign` is not answerable here, because it describes a path no artifact was
|
|
165
165
|
* planned for.
|
|
@@ -231,7 +231,7 @@ export declare const BIN_ENTRY_PATH = "src/bin/main.ts";
|
|
|
231
231
|
*
|
|
232
232
|
* @remarks
|
|
233
233
|
* `src` selects published library environments and `app` selects private
|
|
234
|
-
* runtime environments. The
|
|
234
|
+
* runtime environments. The axes are independent, so library-only,
|
|
235
235
|
* application-only, and mixed workspaces are all first class. `dependencies`
|
|
236
236
|
* are runtime `@orkestrel/*` packages. A peer in the `@orkestrel` scope is a
|
|
237
237
|
* fleet pin; every other peer is a floor. `extras` are package-specific
|
|
@@ -313,7 +313,7 @@ export declare function blueprintToConfigArtifacts(blueprint: Blueprint): readon
|
|
|
313
313
|
* not installed by the workspace that declares it and developing against one
|
|
314
314
|
* requires it present. A runtime dependency is the opposite case and is removed:
|
|
315
315
|
* it is already installed, so a second declaration would state one fact twice
|
|
316
|
-
* and the
|
|
316
|
+
* and the ranges would be free to disagree.
|
|
317
317
|
*
|
|
318
318
|
* A workspace never declares itself, so its own package name is removed. That
|
|
319
319
|
* matters for a workspace named after a package the baseline already carries:
|
|
@@ -351,7 +351,7 @@ export declare function blueprintToGuideArtifacts(blueprint: Blueprint): readonl
|
|
|
351
351
|
* Derive the host-specific machinery a generated root Vite configuration carries.
|
|
352
352
|
*
|
|
353
353
|
* @param blueprint - The workspace specification.
|
|
354
|
-
* @returns The
|
|
354
|
+
* @returns The pipelines the generated configuration selects.
|
|
355
355
|
*
|
|
356
356
|
* @remarks
|
|
357
357
|
* The sole derivation of that set: every renderer reads it rather than
|
|
@@ -396,7 +396,7 @@ export declare function blueprintToMachinery(blueprint: Blueprint): ViteMachiner
|
|
|
396
396
|
* artifact serialized directly.
|
|
397
397
|
*
|
|
398
398
|
* The artifact carrying this text is claimed by birth. A workspace owns its own
|
|
399
|
-
* manifest
|
|
399
|
+
* manifest after it exists: its description, its keywords, and any script it
|
|
400
400
|
* added are the consumer's, so a repair that replaced the file would take them.
|
|
401
401
|
* The one part scaffold keeps current afterwards is the declared `@orkestrel/*`
|
|
402
402
|
* range set, and that is a region with its own writer rather than a claim over
|
|
@@ -437,14 +437,14 @@ export declare function blueprintToOrchestrationArtifacts(blueprint: Blueprint):
|
|
|
437
437
|
* Only the laws a blueprint answers alone are here. The structural record and
|
|
438
438
|
* its bounds are already settled by `isBlueprint`, which refuses a value that is
|
|
439
439
|
* not a blueprint at all; what remains is the syntax of a name, a version, a
|
|
440
|
-
* range, and an engines floor, the combinations the
|
|
441
|
-
* and the overlaps between the
|
|
440
|
+
* range, and an engines floor, the combinations the environment axes admit,
|
|
441
|
+
* and the overlaps between the declared package lists. The laws that need
|
|
442
442
|
* a drafted plan belong to {@link artifactsToQuestions} and
|
|
443
443
|
* {@link overridesToQuestions}.
|
|
444
444
|
*
|
|
445
445
|
* A question blocks when it describes a workspace this package cannot generate.
|
|
446
|
-
*
|
|
447
|
-
*
|
|
446
|
+
* These do not, because each describes a workspace it can describe honestly but
|
|
447
|
+
* will not create: a published axis of several environments without core,
|
|
448
448
|
* whose manifest names a core build the workspace never runs; a showcase flag
|
|
449
449
|
* whose required browser axis is absent, which emits nothing; and an
|
|
450
450
|
* integration flag over fewer than two environments, whose seed does emit and
|
|
@@ -555,7 +555,7 @@ export declare function blueprintToScripts(blueprint: Blueprint): Readonly<Recor
|
|
|
555
555
|
*
|
|
556
556
|
* @remarks
|
|
557
557
|
* The barrels and every runtime entry intentionally hold nothing. A generated
|
|
558
|
-
* sample entity
|
|
558
|
+
* sample entity would read as package implementation, so the
|
|
559
559
|
* scaffold establishes only the selected environment boundaries. An application
|
|
560
560
|
* entry is empty for the same reason the bin entry is, and because the vendored
|
|
561
561
|
* lint config refuses an unassigned import outside a stylesheet, so the entry
|
|
@@ -683,8 +683,8 @@ export declare type CatalogEntry = {
|
|
|
683
683
|
*
|
|
684
684
|
* The order matters because these packages are `0.0.x`, where a caret pins one
|
|
685
685
|
* exact release. Publishing a dependent before its dependency leaves the
|
|
686
|
-
* dependent pinned to the older release, and
|
|
687
|
-
*
|
|
686
|
+
* dependent pinned to the older release, and ranges that disagree install
|
|
687
|
+
* duplicate copies of one package that the compiler reads as distinct types.
|
|
688
688
|
*
|
|
689
689
|
* A cycle cannot be published in rounds, so its members are omitted rather than
|
|
690
690
|
* placed in an order that would be wrong. An absent name is the report: compare
|
|
@@ -793,7 +793,7 @@ export declare interface CompileFailure {
|
|
|
793
793
|
* `INVALID`. Structure raises; the laws a well-formed blueprint can still break
|
|
794
794
|
* are the gate's, and they answer with questions.
|
|
795
795
|
*
|
|
796
|
-
*
|
|
796
|
+
* The consequences of that order are worth stating, because they are the ones a
|
|
797
797
|
* JavaScript caller meets first. A property backed by an accessor is refused
|
|
798
798
|
* rather than read, which is what closes the race a guard cannot close from
|
|
799
799
|
* inside; the accessor never runs. And an optional field present with the value
|
|
@@ -845,21 +845,21 @@ export declare class Compiler implements CompilerInterface {
|
|
|
845
845
|
* carries no plan, then `compile` with the whole outcome either way, so an
|
|
846
846
|
* observer reads every compile from one event and the refusals from the other.
|
|
847
847
|
*
|
|
848
|
-
* A plan says the blueprint can be built. It does not
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
* Nothing downstream repeats that check, because only the caller knows which
|
|
854
|
-
*
|
|
848
|
+
* A plan says the blueprint can be built. It does not decide whether to create
|
|
849
|
+
* it, and the questions beside it are what this compiler could not settle. A
|
|
850
|
+
* caller creating a fresh workspace answers them first and writes nothing while
|
|
851
|
+
* any remains, which is the rule the `new` verb applies; a caller describing or
|
|
852
|
+
* repairing an existing target carries them through instead.
|
|
853
|
+
* Nothing downstream repeats that check, because only the caller knows which
|
|
854
|
+
* case it is.
|
|
855
855
|
*
|
|
856
856
|
* @example
|
|
857
857
|
* ```ts
|
|
858
|
-
* import {
|
|
858
|
+
* import { Compiler, createBlueprint } from '@orkestrel/scaffold'
|
|
859
859
|
*
|
|
860
860
|
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
861
861
|
*
|
|
862
|
-
*
|
|
862
|
+
* new Compiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
|
|
863
863
|
* ```
|
|
864
864
|
*/
|
|
865
865
|
compile(blueprint: Blueprint, groups?: readonly Group[]): Scaffolding;
|
|
@@ -885,12 +885,12 @@ export declare class Compiler implements CompilerInterface {
|
|
|
885
885
|
*
|
|
886
886
|
* @example
|
|
887
887
|
* ```ts
|
|
888
|
-
* import {
|
|
888
|
+
* import { Compiler, createBlueprint } from '@orkestrel/scaffold'
|
|
889
889
|
*
|
|
890
890
|
* const blueprint = createBlueprint('router', { src: ['core'] })
|
|
891
891
|
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
892
|
+
* new Compiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
|
|
893
|
+
* new Compiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
|
|
894
894
|
* ```
|
|
895
895
|
*/
|
|
896
896
|
audit(blueprint: Blueprint, current: Snapshot, groups?: readonly Group[]): Audit;
|
|
@@ -901,9 +901,9 @@ export declare class Compiler implements CompilerInterface {
|
|
|
901
901
|
*
|
|
902
902
|
* @example
|
|
903
903
|
* ```ts
|
|
904
|
-
* import {
|
|
904
|
+
* import { Compiler } from '@orkestrel/scaffold'
|
|
905
905
|
*
|
|
906
|
-
* const compiler =
|
|
906
|
+
* const compiler = new Compiler()
|
|
907
907
|
* compiler.destroy()
|
|
908
908
|
* compiler.emitter.destroyed // true
|
|
909
909
|
* ```
|
|
@@ -967,7 +967,7 @@ export declare class Compiler implements CompilerInterface {
|
|
|
967
967
|
readonly error?: EmitterErrorHandler;
|
|
968
968
|
}
|
|
969
969
|
|
|
970
|
-
/** The
|
|
970
|
+
/** The compile phases, in the order they run. */
|
|
971
971
|
export declare type CompileStage = 'draft' | 'gate' | 'pin';
|
|
972
972
|
|
|
973
973
|
/**
|
|
@@ -1043,15 +1043,15 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1043
1043
|
}>;
|
|
1044
1044
|
factories: Readonly<{
|
|
1045
1045
|
src: Readonly<{
|
|
1046
|
-
core: "export const srcCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:core', color: 'magenta' },\n\t\t\t\tinclude: ['tests/src/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";
|
|
1047
|
-
browser: "export const srcBrowser = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/browser/index.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'index.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/browser',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:browser', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/browser/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n{{global}}\n\t\t\t\tbrowser: {\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t\t},\n\t\t\t\tfileParallelism: false,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
1048
|
-
server: "export const srcServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/server/index.ts'),\n\t\t\t\t\tformats: ['es', 'cjs'],\n\t\t\t\t\tfileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/server',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tplatform: 'node',\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:server', color: 'red' },\n\t\t\t\tinclude: ['tests/src/server/**/*.test.ts'],\n{{exclude}}\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";
|
|
1049
|
-
bin: "export const srcBin = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/bin')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/bin/main.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'main.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/bin',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\texternal: (id: string) =>\n\t\t\t\t\t\tid.startsWith('node:') ||\n\t\t\t\t\t\tid.startsWith('@orkestrel/') ||\n\t\t\t\t\t\tid.startsWith('@src/') ||\n\t\t\t\t\t\tpeers.some((peer) => id === peer || id.startsWith(peer + '/')),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:bin', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/bin/**/*.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\t// A bin test drives the real executable over a real temporary repository, so it\n\t\t\t\t// spends seconds in process startup and filesystem work rather than milliseconds.\n\t\t\t\t// Vitest's five-second default clears one alone and times out under a full suite.\n\t\t\t\ttestTimeout: 15_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
1046
|
+
core: "export const srcCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\trolldownOptions: { onLog: enforceBuildLog },\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:core', color: 'magenta' },\n\t\t\t\tinclude: ['tests/src/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";
|
|
1047
|
+
browser: "export const srcBrowser = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/browser/index.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'index.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/browser',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:browser', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/browser/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n{{global}}\n\t\t\t\tbrowser: {\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t\t},\n\t\t\t\tfileParallelism: false,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
1048
|
+
server: "export const srcServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/server/index.ts'),\n\t\t\t\t\tformats: ['es', 'cjs'],\n\t\t\t\t\tfileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/server',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\tplatform: 'node',\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:server', color: 'red' },\n\t\t\t\tinclude: ['tests/src/server/**/*.test.ts'],\n{{exclude}}\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";
|
|
1049
|
+
bin: "export const srcBin = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/bin')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/bin/main.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'main.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/bin',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\texternal: (id: string) =>\n\t\t\t\t\t\tid.startsWith('node:') ||\n\t\t\t\t\t\tid.startsWith('@orkestrel/') ||\n\t\t\t\t\t\tid.startsWith('@src/') ||\n\t\t\t\t\t\tpeers.some((peer) => id === peer || id.startsWith(peer + '/')),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:bin', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/bin/**/*.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\t// A bin test drives the real executable over a real temporary repository, so it\n\t\t\t\t// spends seconds in process startup and filesystem work rather than milliseconds.\n\t\t\t\t// Vitest's five-second default clears one alone and times out under a full suite.\n\t\t\t\ttestTimeout: 15_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
1050
1050
|
}>;
|
|
1051
1051
|
app: Readonly<{
|
|
1052
1052
|
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";
|
|
1053
|
-
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: {
|
|
1054
|
-
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: {
|
|
1053
|
+
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: {\n\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\tinput: resolveWorkspacePath('app/browser/index.html'),\n\t\t\t},\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(browserOptions),\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(): UserConfig {\n\treturn applicationBrowser(false)\n}\n{{showcaseFactory}}";
|
|
1054
|
+
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: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\texternal: (id: string) => id.startsWith('node:'),\n\t\t\t\t},\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";
|
|
1055
1055
|
}>;
|
|
1056
1056
|
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";
|
|
1057
1057
|
config: "export const config = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\ttest: {\n\t\t\t\tname: { label: 'config', color: 'yellow' },\n\t\t\t\tinclude: ['tests/config.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\t// A config test validates every target wrapper and runs the real linter twice with\n\t\t\t\t// 15-second child caps, so this budget clears both caps and reports their diagnostics.\n\t\t\t\ttestTimeout: 45_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
|
|
@@ -1082,14 +1082,14 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1082
1082
|
browser: "import { defineConfig } from 'vite'\nimport dts from 'vite-plugin-dts'\nimport { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'\n\n// vite-plugin-dts rolls this face into one declaration, and the roll-up reaches\n// src/core through a relative source path the tarball does not carry. The path\n// keeps each source module's own depth, so a module in a browser subfolder emits\n// one that leaves dist/src entirely. The rewrite below externalizes core through\n// the package's own published root export, on the final roll-up only.\nexport default defineConfig(\n\tsrcBrowser({\n\t\tplugins: [\n\t\t\tdts({\n\t\t\t\ttsconfigPath: resolveWorkspacePath('configs/src/tsconfig.browser.json'),\n\t\t\t\tbundleTypes: true,\n\t\t\t\tbeforeWriteFile: (path, content) => ({\n\t\t\t\t\tcontent: /[\\\\/]dist[\\\\/]src[\\\\/]browser[\\\\/]index\\.d\\.ts$/.test(path)\n{{replacement}}\n\t\t\t\t\t\t: content,\n\t\t\t\t}),\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
|
|
1083
1083
|
server: "import { defineConfig } from 'vite'\nimport dts from 'vite-plugin-dts'\nimport { srcServer, resolveWorkspacePath } from '../../vite.config.ts'\n\n// vite-plugin-dts rolls this face into one declaration, and the roll-up reaches\n// src/core through a relative source path the tarball does not carry. The rewrite\n// below externalizes core through the package's own published root export, on the\n// final roll-up only.\nexport default defineConfig(\n\tsrcServer({\n\t\tplugins: [\n\t\t\tdts({\n\t\t\t\ttsconfigPath: resolveWorkspacePath('configs/src/tsconfig.server.json'),\n\t\t\t\tbundleTypes: true,\n\t\t\t\tbeforeWriteFile: (path, content) => ({\n\t\t\t\t\tcontent: /[\\\\/]dist[\\\\/]src[\\\\/]server[\\\\/]index\\.d\\.ts$/.test(path)\n{{replacement}}\n\t\t\t\t\t\t: content,\n\t\t\t\t}),\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
|
|
1084
1084
|
}>;
|
|
1085
|
-
bin: "import { defineConfig } from 'vite'\nimport { srcBin } from '../../vite.config.ts'\n\n// The `scaffold` executable build — a single ESM lib file, no declarations (an\n// executable ships no types), with the `#!/usr/bin/env node` shebang re-emitted
|
|
1085
|
+
bin: "import { defineConfig } from 'vite'\nimport { srcBin } from '../../vite.config.ts'\n\n// The `scaffold` executable build — a single ESM lib file, no declarations (an\n// executable ships no types), with the `#!/usr/bin/env node` shebang re-emitted through\n// `output.banner` (rolldown strips shebangs from source during bundling), and\n// `output.paths` rewriting the externalized `@src/*` specifiers to the built sibling\n// src environments (relative to `dist/bin/`), so the emitted bin resolves at runtime.\nexport default defineConfig(\n\tsrcBin({\n\t\tbuild: {\n\t\t\trolldownOptions: {\n\t\t\t\toutput: {\n\t\t\t\t\tbanner: '#!/usr/bin/env node',\n{{paths}}\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}),\n)\n";
|
|
1086
1086
|
app: Readonly<{
|
|
1087
1087
|
browser: "import { defineConfig } from 'vite'\nimport { appBrowser } from '../../vite.config.ts'\n\nexport default defineConfig(appBrowser())\n";
|
|
1088
1088
|
server: "import { defineConfig } from 'vite'\nimport { appServer } from '../../vite.config.ts'\n\nexport default defineConfig(appServer())\n";
|
|
1089
1089
|
showcase: "import { defineConfig } from 'vite'\nimport { appShowcase } from '../../vite.config.ts'\n\nexport default defineConfig(appShowcase())\n";
|
|
1090
1090
|
}>;
|
|
1091
1091
|
}>;
|
|
1092
|
-
browsers: "// A generated browser workspace resolves its own Chromium here rather than in\n// `configs/helpers.ts`, because that leaf is vendored byte-identical to every\n// workspace and most of them declare no `playwright` to import.\n\nimport type { PlaywrightProviderOptions } from '@vitest/browser-playwright'\nimport { chromium } from 'playwright'\nimport { accessSync, constants as FS_CONSTANTS, globSync, readdirSync, statSync } from 'node:fs'\nimport { basename, dirname, join, resolve as resolvePath } from 'node:path'\n\n/**\n * Chromium executable layouts inside a `chromium-<revision>` browsers-directory entry, per\n * platform.\n *\n * @remarks\n * The current Playwright build ships Chrome for Testing on macOS. The trailing `Chromium.app`\n * layouts are what earlier builds shipped, so the list spans Playwright versions instead of\n * pinning to the installed one.\n */\nexport const CHROMIUM_LAYOUTS = Object.freeze([\n\t'chrome-linux/chrome',\n\t'chrome-linux64/chrome',\n\t'chrome-win/chrome.exe',\n\t'chrome-win64/chrome.exe',\n\t'chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac/Chromium.app/Contents/MacOS/Chromium',\n\t'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',\n])\n\n/** The `chromium-<revision>` entry name Playwright installs one managed build into. */\nexport const CHROMIUM_ENTRY_PATTERN = /^chromium-\\d+$/\n\n/** The revision number carried by any path containing a `chromium-<revision>` segment. */\nexport const CHROMIUM_REVISION_PATTERN = /chromium-(\\d+)/\n\n/** The directory a managed Linux container installs its bundled Playwright browsers into. */\nexport const BUNDLED_BROWSERS_ROOT = '/opt/pw-browsers'\n\n/**\n * Bundled Chromium layouts under the managed-container browsers root, as glob patterns.\n *\n * @remarks\n * The revision directory and its inner layout both drift across Playwright builds, and the\n * container also carries a top-level `chromium` alias, so every known shape is globbed.\n */\nexport const BUNDLED_CHROMIUM_LAYOUTS = Object.freeze([\n\t'chromium',\n\t'chromium-*/chrome-linux64/chrome',\n\t'chromium-*/chrome-linux/chrome',\n])\n\n/** Stable Playwright Chromium channels and their standard executable layouts. */\nexport const SYSTEM_BROWSER_CHANNELS = Object.freeze([\n\tObject.freeze({\n\t\tchannel: 'chrome',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/google/chrome/chrome',\n\t\t\tdarwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n\t\t\twin32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),\n\t\t}),\n\t}),\n\tObject.freeze({\n\t\tchannel: 'msedge',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/microsoft/msedge/msedge',\n\t\t\tdarwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n\t\t\twin32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),\n\t\t}),\n\t}),\n])\n\n/**\n * Determine whether a path identifies an executable regular file.\n *\n * @param path - The filesystem path to inspect.\n * @returns Whether the path is a regular file with execute access.\n *\n * @example\n * ```ts\n * isBrowserExecutable('/opt/google/chrome/chrome')\n * ```\n */\nexport function isBrowserExecutable(path: string): boolean {\n\ttry {\n\t\tif (!statSync(path).isFile()) return false\n\t\taccessSync(path, FS_CONSTANTS.X_OK)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Order two Chromium paths so the highest revision sorts first.\n *\n * @param left - The first path or directory entry to compare.\n * @param right - The second path or directory entry to compare.\n * @returns A negative number when `left` sorts first, positive when `right` does.\n *\n * @remarks\n * Revisions are numbers, so `chromium-1200` outranks `chromium-999` despite sorting below it\n * lexically. A path carrying no revision falls back to descending name order.\n *\n * @example\n * ```ts\n * ['chromium-999', 'chromium-1200'].sort(compareRevisions)\n * ```\n */\nexport function compareRevisions(left: string, right: string): number {\n\tconst leftRevision = CHROMIUM_REVISION_PATTERN.exec(left)?.[1]\n\tconst rightRevision = CHROMIUM_REVISION_PATTERN.exec(right)?.[1]\n\tif (leftRevision === undefined || rightRevision === undefined) return right.localeCompare(left)\n\treturn Number(rightRevision) - Number(leftRevision)\n}\n\n/**\n * Read the executable path of Playwright's pinned Chromium revision.\n *\n * @returns The pinned executable path, or `undefined` when this platform has none.\n *\n * @remarks\n * Playwright throws rather than returning a path when the current platform carries no initialized\n * executable, and an unguarded call would fail configuration evaluation for every project.\n *\n * @example\n * ```ts\n * resolvePinnedBrowser()\n * ```\n */\nexport function resolvePinnedBrowser(): string | undefined {\n\ttry {\n\t\tconst pinned = chromium.executablePath()\n\t\treturn pinned.length === 0 ? undefined : pinned\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,\n * otherwise a `chromium` / `chromium.exe` alias or any other `chromium-*` revision under the same\n * Playwright browsers directory. A pinned-revision miss is not Chromium absence — managed\n * containers ship one usable build, often behind a revision-agnostic alias, for many Playwright\n * versions.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision.\n * @returns The managed executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveManagedBrowser('/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome')\n * ```\n */\nexport function resolveManagedBrowser(pinned: string): string | undefined {\n\tif (isBrowserExecutable(pinned)) return pinned\n\tlet revisionRoot = dirname(pinned)\n\tfor (;;) {\n\t\tif (CHROMIUM_ENTRY_PATTERN.test(basename(revisionRoot))) break\n\t\tconst parent = dirname(revisionRoot)\n\t\tif (parent === revisionRoot) return undefined\n\t\trevisionRoot = parent\n\t}\n\tconst browsersRoot = dirname(revisionRoot)\n\tfor (const alias of ['chromium', 'chromium.exe']) {\n\t\tconst candidate = resolvePath(browsersRoot, alias)\n\t\tif (isBrowserExecutable(candidate)) return candidate\n\t}\n\tlet entries: readonly string[]\n\ttry {\n\t\tentries = readdirSync(browsersRoot)\n\t} catch {\n\t\treturn undefined\n\t}\n\tconst revisions = entries\n\t\t.filter((entry) => CHROMIUM_ENTRY_PATTERN.test(entry))\n\t\t.sort(compareRevisions)\n\tfor (const revision of revisions) {\n\t\tfor (const layout of CHROMIUM_LAYOUTS) {\n\t\t\tconst candidate = resolvePath(browsersRoot, revision, layout)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the Chromium a managed Linux container bundles outside the Playwright cache.\n *\n * @param platform - The Node platform the container runs on.\n * @param root - The bundled browsers directory to search.\n * @returns The highest matching executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveBundledBrowser('linux', BUNDLED_BROWSERS_ROOT)\n * ```\n */\nexport function resolveBundledBrowser(platform: NodeJS.Platform, root: string): string | undefined {\n\tif (platform !== 'linux') return undefined\n\tfor (const layout of BUNDLED_CHROMIUM_LAYOUTS) {\n\t\tlet matches: readonly string[]\n\t\ttry {\n\t\t\tmatches = globSync(layout, { cwd: root })\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t\tfor (const match of [...matches].sort(compareRevisions)) {\n\t\t\tconst candidate = resolvePath(root, match)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the first installed stable system Chromium channel.\n *\n * @param platform - The Node platform whose standard layouts should be probed.\n * @param environment - The process environment supplying Windows installation roots.\n * @returns `chrome`, then `msedge`, or `undefined` when neither is executable.\n *\n * @example\n * ```ts\n * resolveSystemBrowser(process.platform, process.env)\n * ```\n */\nexport function resolveSystemBrowser(\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n): string | undefined {\n\tif (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined\n\tconst roots = new Set<string>()\n\tif (platform === 'win32') {\n\t\tfor (const root of [\n\t\t\tenvironment.LOCALAPPDATA,\n\t\t\tenvironment.PROGRAMFILES,\n\t\t\tenvironment['PROGRAMFILES(X86)'],\n\t\t]) {\n\t\t\tif (root !== undefined && root.length > 0) roots.add(root)\n\t\t}\n\t\tconst homeDrive = environment.HOMEDRIVE\n\t\tif (homeDrive !== undefined && homeDrive.length > 0) {\n\t\t\troots.add(join(homeDrive, 'Program Files'))\n\t\t\troots.add(join(homeDrive, 'Program Files (x86)'))\n\t\t}\n\t}\n\tfor (const browser of SYSTEM_BROWSER_CHANNELS) {\n\t\tif (platform === 'win32') {\n\t\t\tfor (const root of roots) {\n\t\t\t\tif (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif (isBrowserExecutable(browser.layouts[platform])) return browser.channel\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve Playwright provider options for whatever browser this host can actually launch.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision, when it has one.\n * @param platform - The Node platform whose standard layouts should be probed.\n * @param environment - The process environment supplying operator overrides and Windows roots.\n * @param root - The managed-container bundled browsers directory to search.\n * @returns Provider options naming an executable, a WebSocket endpoint, or a channel.\n *\n * @remarks\n * Precedence, most important first: `PLAYWRIGHT_EXECUTABLE_PATH`, `PLAYWRIGHT_WS_ENDPOINT`,\n * `PLAYWRIGHT_CHANNEL`, the managed Playwright Chromium, the container's bundled Chromium, a\n * verified system channel, then the platform default channel. An operator override outranks\n * discovery and is returned exactly as given: none of those three environment values is checked\n * against the filesystem, because verifying an override would defeat the override. The pinned\n * managed revision outranks anything found on the host because it is deterministic. The installed\n * pinned revision returns empty options so Playwright keeps its own default launch semantics. Only\n * a discovered system channel is verified before it is named. The platform default is unverified\n * as well and exists only as a last resort: Windows takes `msedge`, which ships with the OS and\n * never collides with a foreground Chrome.\n *\n * @example\n * ```ts\n * resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n * ```\n */\nexport function resolveBrowser(\n\tpinned: string | undefined,\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n\troot: string = BUNDLED_BROWSERS_ROOT,\n): PlaywrightProviderOptions {\n\tconst executable = environment.PLAYWRIGHT_EXECUTABLE_PATH\n\tif (executable !== undefined && executable.length > 0) {\n\t\treturn { launchOptions: { executablePath: executable } }\n\t}\n\tconst endpoint = environment.PLAYWRIGHT_WS_ENDPOINT\n\tif (endpoint !== undefined && endpoint.length > 0) {\n\t\treturn { connectOptions: { wsEndpoint: endpoint } }\n\t}\n\tconst requested = environment.PLAYWRIGHT_CHANNEL\n\tif (requested !== undefined && requested.length > 0) {\n\t\treturn { launchOptions: { channel: requested } }\n\t}\n\tconst managed = pinned === undefined ? undefined : resolveManagedBrowser(pinned)\n\tif (managed !== undefined) {\n\t\treturn managed === pinned ? {} : { launchOptions: { executablePath: managed } }\n\t}\n\tconst bundled = resolveBundledBrowser(platform, root)\n\tif (bundled !== undefined) return { launchOptions: { executablePath: bundled } }\n\tconst fallback = platform === 'win32' ? 'msedge' : 'chrome'\n\treturn { launchOptions: { channel: resolveSystemBrowser(platform, environment) ?? fallback } }\n}\n";
|
|
1092
|
+
browsers: "// A generated browser workspace resolves its own Chromium here rather than in\n// `configs/helpers.ts`, because that leaf is vendored byte-identical to every\n// workspace and most of them declare no `playwright` to import.\n\nimport type { PlaywrightProviderOptions } from '@vitest/browser-playwright'\nimport { chromium } from 'playwright'\nimport { accessSync, constants as FS_CONSTANTS, globSync, readdirSync, statSync } from 'node:fs'\nimport { basename, dirname, join, resolve as resolvePath } from 'node:path'\n\n/**\n * Chromium executable layouts inside a `chromium-<revision>` browsers-directory entry, per\n * platform.\n *\n * @remarks\n * The current Playwright build ships Chrome for Testing on macOS. The trailing `Chromium.app`\n * layouts are what earlier builds shipped, so the list spans Playwright versions instead of\n * pinning to the installed one.\n */\nexport const CHROMIUM_LAYOUTS = Object.freeze([\n\t'chrome-linux/chrome',\n\t'chrome-linux64/chrome',\n\t'chrome-win/chrome.exe',\n\t'chrome-win64/chrome.exe',\n\t'chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac/Chromium.app/Contents/MacOS/Chromium',\n\t'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',\n])\n\n/** The `chromium-<revision>` entry name Playwright installs one managed build into. */\nexport const CHROMIUM_ENTRY_PATTERN = /^chromium-\\d+$/\n\n/** The revision number carried by any path containing a `chromium-<revision>` segment. */\nexport const CHROMIUM_REVISION_PATTERN = /chromium-(\\d+)/\n\n/** The directory a managed Linux container installs its bundled Playwright browsers into. */\nexport const BUNDLED_BROWSERS_ROOT = '/opt/pw-browsers'\n\n/**\n * Bundled Chromium layouts under the managed-container browsers root, as glob patterns.\n *\n * @remarks\n * The revision directory and its inner layout both drift across Playwright builds, and the\n * container also carries a top-level `chromium` alias, so every known shape is globbed.\n */\nexport const BUNDLED_CHROMIUM_LAYOUTS = Object.freeze([\n\t'chromium',\n\t'chromium-*/chrome-linux64/chrome',\n\t'chromium-*/chrome-linux/chrome',\n])\n\n/** Stable Playwright Chromium channels and their standard executable layouts. */\nexport const SYSTEM_BROWSER_CHANNELS = Object.freeze([\n\tObject.freeze({\n\t\tchannel: 'chrome',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/google/chrome/chrome',\n\t\t\tdarwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n\t\t\twin32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),\n\t\t}),\n\t}),\n\tObject.freeze({\n\t\tchannel: 'msedge',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/microsoft/msedge/msedge',\n\t\t\tdarwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n\t\t\twin32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),\n\t\t}),\n\t}),\n])\n\n/**\n * Determine whether a path identifies an executable regular file.\n *\n * @param path - The filesystem path to inspect.\n * @returns Whether the path is a regular file with execute access.\n *\n * @example\n * ```ts\n * isBrowserExecutable('/opt/google/chrome/chrome')\n * ```\n */\nexport function isBrowserExecutable(path: string): boolean {\n\ttry {\n\t\tif (!statSync(path).isFile()) return false\n\t\taccessSync(path, FS_CONSTANTS.X_OK)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Order two Chromium paths so the highest revision sorts first.\n *\n * @param left - The first path or directory entry to compare.\n * @param right - The second path or directory entry to compare.\n * @returns A negative number when `left` sorts first, positive when `right` does.\n *\n * @remarks\n * Revisions are numbers, so `chromium-1200` outranks `chromium-999` despite sorting below it\n * lexically. A path carrying no revision falls back to descending name order.\n *\n * @example\n * ```ts\n * ['chromium-999', 'chromium-1200'].sort(compareRevisions)\n * ```\n */\nexport function compareRevisions(left: string, right: string): number {\n\tconst leftRevision = CHROMIUM_REVISION_PATTERN.exec(left)?.[1]\n\tconst rightRevision = CHROMIUM_REVISION_PATTERN.exec(right)?.[1]\n\tif (leftRevision === undefined || rightRevision === undefined) return right.localeCompare(left)\n\treturn Number(rightRevision) - Number(leftRevision)\n}\n\n/**\n * Read the executable path of Playwright's pinned Chromium revision.\n *\n * @returns The pinned executable path, or `undefined` when this platform has none.\n *\n * @remarks\n * Playwright throws rather than returning a path when the current platform carries no initialized\n * executable, and an unguarded call would fail configuration evaluation for every project.\n *\n * @example\n * ```ts\n * resolvePinnedBrowser()\n * ```\n */\nexport function resolvePinnedBrowser(): string | undefined {\n\ttry {\n\t\tconst pinned = chromium.executablePath()\n\t\treturn pinned.length === 0 ? undefined : pinned\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,\n * otherwise a `chromium` / `chromium.exe` alias or any other `chromium-*` revision under the same\n * Playwright browsers directory. A pinned-revision miss is not Chromium absence — managed\n * containers ship one usable build, often behind a revision-agnostic alias, for many Playwright\n * versions.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision.\n * @returns The managed executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveManagedBrowser('/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome')\n * ```\n */\nexport function resolveManagedBrowser(pinned: string): string | undefined {\n\tif (isBrowserExecutable(pinned)) return pinned\n\tlet revisionRoot = dirname(pinned)\n\tfor (;;) {\n\t\tif (CHROMIUM_ENTRY_PATTERN.test(basename(revisionRoot))) break\n\t\tconst parent = dirname(revisionRoot)\n\t\tif (parent === revisionRoot) return undefined\n\t\trevisionRoot = parent\n\t}\n\tconst browsersRoot = dirname(revisionRoot)\n\tfor (const alias of ['chromium', 'chromium.exe']) {\n\t\tconst candidate = resolvePath(browsersRoot, alias)\n\t\tif (isBrowserExecutable(candidate)) return candidate\n\t}\n\tlet entries: readonly string[]\n\ttry {\n\t\tentries = readdirSync(browsersRoot)\n\t} catch {\n\t\treturn undefined\n\t}\n\tconst revisions = entries\n\t\t.filter((entry) => CHROMIUM_ENTRY_PATTERN.test(entry))\n\t\t.sort(compareRevisions)\n\tfor (const revision of revisions) {\n\t\tfor (const layout of CHROMIUM_LAYOUTS) {\n\t\t\tconst candidate = resolvePath(browsersRoot, revision, layout)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the Chromium a managed Linux container bundles outside the Playwright cache.\n *\n * @param platform - The Node platform the container runs on.\n * @param root - The bundled browsers directory to search.\n * @returns The highest matching executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveBundledBrowser('linux', BUNDLED_BROWSERS_ROOT)\n * ```\n */\nexport function resolveBundledBrowser(platform: NodeJS.Platform, root: string): string | undefined {\n\tif (platform !== 'linux') return undefined\n\tfor (const layout of BUNDLED_CHROMIUM_LAYOUTS) {\n\t\tlet matches: readonly string[]\n\t\ttry {\n\t\t\tmatches = globSync(layout, { cwd: root })\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t\tfor (const match of [...matches].sort(compareRevisions)) {\n\t\t\tconst candidate = resolvePath(root, match)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the first installed stable system Chromium channel.\n *\n * @param platform - The Node platform whose standard layouts this call probes.\n * @param environment - The process environment supplying Windows installation roots.\n * @returns `chrome`, then `msedge`, or `undefined` when neither is executable.\n *\n * @example\n * ```ts\n * resolveSystemBrowser(process.platform, process.env)\n * ```\n */\nexport function resolveSystemBrowser(\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n): string | undefined {\n\tif (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined\n\tconst roots = new Set<string>()\n\tif (platform === 'win32') {\n\t\tfor (const root of [\n\t\t\tenvironment.LOCALAPPDATA,\n\t\t\tenvironment.PROGRAMFILES,\n\t\t\tenvironment['PROGRAMFILES(X86)'],\n\t\t]) {\n\t\t\tif (root !== undefined && root.length > 0) roots.add(root)\n\t\t}\n\t\tconst homeDrive = environment.HOMEDRIVE\n\t\tif (homeDrive !== undefined && homeDrive.length > 0) {\n\t\t\troots.add(join(homeDrive, 'Program Files'))\n\t\t\troots.add(join(homeDrive, 'Program Files (x86)'))\n\t\t}\n\t}\n\tfor (const browser of SYSTEM_BROWSER_CHANNELS) {\n\t\tif (platform === 'win32') {\n\t\t\tfor (const root of roots) {\n\t\t\t\tif (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif (isBrowserExecutable(browser.layouts[platform])) return browser.channel\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve Playwright provider options for whatever browser this host can actually launch.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision, when it has one.\n * @param platform - The Node platform whose standard layouts this call probes.\n * @param environment - The process environment supplying operator overrides and Windows roots.\n * @param root - The managed-container bundled browsers directory to search.\n * @returns Provider options naming an executable, a WebSocket endpoint, or a channel.\n *\n * @remarks\n * Precedence, most important first: `PLAYWRIGHT_EXECUTABLE_PATH`, `PLAYWRIGHT_WS_ENDPOINT`,\n * `PLAYWRIGHT_CHANNEL`, the managed Playwright Chromium, the container's bundled Chromium, a\n * verified system channel, then the platform default channel. An operator override outranks\n * discovery and is returned exactly as given: none of those environment values is checked\n * against the filesystem, because verifying an override would defeat the override. The pinned\n * managed revision outranks anything found on the host because it is deterministic. The installed\n * pinned revision returns empty options so Playwright keeps its own default launch semantics. Only\n * a discovered system channel is verified before it is named. The platform default is unverified\n * as well and exists only as a last resort: Windows takes `msedge`, which ships with the OS and\n * never collides with a foreground Chrome.\n *\n * @example\n * ```ts\n * resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n * ```\n */\nexport function resolveBrowser(\n\tpinned: string | undefined,\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n\troot: string = BUNDLED_BROWSERS_ROOT,\n): PlaywrightProviderOptions {\n\tconst executable = environment.PLAYWRIGHT_EXECUTABLE_PATH\n\tif (executable !== undefined && executable.length > 0) {\n\t\treturn { launchOptions: { executablePath: executable } }\n\t}\n\tconst endpoint = environment.PLAYWRIGHT_WS_ENDPOINT\n\tif (endpoint !== undefined && endpoint.length > 0) {\n\t\treturn { connectOptions: { wsEndpoint: endpoint } }\n\t}\n\tconst requested = environment.PLAYWRIGHT_CHANNEL\n\tif (requested !== undefined && requested.length > 0) {\n\t\treturn { launchOptions: { channel: requested } }\n\t}\n\tconst managed = pinned === undefined ? undefined : resolveManagedBrowser(pinned)\n\tif (managed !== undefined) {\n\t\treturn managed === pinned ? {} : { launchOptions: { executablePath: managed } }\n\t}\n\tconst bundled = resolveBundledBrowser(platform, root)\n\tif (bundled !== undefined) return { launchOptions: { executablePath: bundled } }\n\tconst fallback = platform === 'win32' ? 'msedge' : 'chrome'\n\treturn { launchOptions: { channel: resolveSystemBrowser(platform, environment) ?? fallback } }\n}\n";
|
|
1093
1093
|
}>;
|
|
1094
1094
|
|
|
1095
1095
|
/** The official-tooling drift proof whose presence makes a workspace `conformance`. */
|
|
@@ -1143,17 +1143,17 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1143
1143
|
* workspace actually declares.
|
|
1144
1144
|
*
|
|
1145
1145
|
* This is the construction door, and {@link parseBlueprint} is the coercing one.
|
|
1146
|
-
* They differ in
|
|
1146
|
+
* They differ in every part: this fills the defaults and takes a
|
|
1147
1147
|
* partial specification, where the parser fills nothing and takes an untrusted
|
|
1148
1148
|
* value; and this refuses by throwing, where the parser refuses by answering
|
|
1149
1149
|
* `undefined`. What they share is the law — both accept exactly what
|
|
1150
1150
|
* `isBlueprint` accepts.
|
|
1151
1151
|
*
|
|
1152
1152
|
* That law is structural only. Whether the name is a name, the version a
|
|
1153
|
-
* version, and the
|
|
1153
|
+
* version, and the environment axes a combination this package can generate
|
|
1154
1154
|
* are the gate's laws, and the gate answers them with {@link Question}s carrying
|
|
1155
|
-
* their accepted candidates. Deciding them here as well would
|
|
1156
|
-
*
|
|
1155
|
+
* their accepted candidates. Deciding them here as well would restate that law
|
|
1156
|
+
* and let the answers disagree, so a blueprint the gate will
|
|
1157
1157
|
* refuse is still constructible.
|
|
1158
1158
|
*
|
|
1159
1159
|
* @example
|
|
@@ -1166,1704 +1166,1685 @@ export declare class Compiler implements CompilerInterface {
|
|
|
1166
1166
|
*/
|
|
1167
1167
|
export declare function createBlueprint(name: string, input?: Partial<Omit<Blueprint, 'name'>>): Blueprint;
|
|
1168
1168
|
|
|
1169
|
-
/**
|
|
1170
|
-
|
|
1171
|
-
*
|
|
1172
|
-
* @param options - The initial listeners and the listener-error handler.
|
|
1173
|
-
* @returns The compiler, typed as the contract consumers program against.
|
|
1174
|
-
* @throws {@link ScaffoldError} coded `INVALID` when `options` is present but is
|
|
1175
|
-
* not an option bag the compiler accepts.
|
|
1176
|
-
*
|
|
1177
|
-
* @example
|
|
1178
|
-
* ```ts
|
|
1179
|
-
* import { createCompiler } from '@orkestrel/scaffold'
|
|
1180
|
-
*
|
|
1181
|
-
* const compiler = createCompiler({ on: { block: (questions) => report(questions) } })
|
|
1182
|
-
* compiler.destroy()
|
|
1183
|
-
* ```
|
|
1184
|
-
*/
|
|
1185
|
-
export declare function createCompiler(options?: CompilerOptions): CompilerInterface;
|
|
1186
|
-
|
|
1187
|
-
/** The development dependencies that emit declarations for published source or an executable. */
|
|
1188
|
-
export declare const DECLARATION_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
1169
|
+
/** The development dependencies that emit declarations for published source or an executable. */
|
|
1170
|
+
export declare const DECLARATION_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
1189
1171
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1172
|
+
/** The `engines.node` range a workspace starts with. */
|
|
1173
|
+
export declare const DEFAULT_ENGINES = ">=22.12.0";
|
|
1192
1174
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* Measure one declared package list against the name and range syntax it accepts.
|
|
1198
|
-
*
|
|
1199
|
-
* @param dependencies - The declared list.
|
|
1200
|
-
* @param field - The blueprint field the list came from, reported on each question.
|
|
1201
|
-
* @param name - The package-name syntax the field accepts.
|
|
1202
|
-
* @param range - The range syntax the field accepts.
|
|
1203
|
-
* @returns One blocking question per rejected name, repeated name, and rejected
|
|
1204
|
-
* range, in list order.
|
|
1205
|
-
*
|
|
1206
|
-
* @remarks
|
|
1207
|
-
* The declared lists and peer partitions differ only in the two syntaxes they
|
|
1208
|
-
* accept, so the rules live here once and each caller supplies its own patterns.
|
|
1209
|
-
* A runtime dependency name reaches a path through its guide mirror and is
|
|
1210
|
-
* fixed to the `@orkestrel` scope. A foreign peer or development extra reaches
|
|
1211
|
-
* no path and admits any valid npm name.
|
|
1212
|
-
*
|
|
1213
|
-
* Both patterns must be stateless. A global or sticky pattern carries a
|
|
1214
|
-
* `lastIndex` between calls, so it would answer differently for the same input
|
|
1215
|
-
* depending on what was tested before it.
|
|
1216
|
-
*
|
|
1217
|
-
* @example
|
|
1218
|
-
* ```ts
|
|
1219
|
-
* import { DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN } from '@orkestrel/scaffold'
|
|
1220
|
-
* import { dependenciesToQuestions } from '@orkestrel/scaffold'
|
|
1221
|
-
*
|
|
1222
|
-
* dependenciesToQuestions(
|
|
1223
|
-
* [{ name: '@orkestrel/router', range: '0.0.8' }],
|
|
1224
|
-
* 'dependencies',
|
|
1225
|
-
* DEPENDENCY_NAME_PATTERN,
|
|
1226
|
-
* ORKESTREL_RANGE_PATTERN,
|
|
1227
|
-
* ).length // 1 — the range is not caret-pinned
|
|
1228
|
-
* ```
|
|
1229
|
-
*/
|
|
1230
|
-
export declare function dependenciesToQuestions(dependencies: readonly Dependency[], field: string, name: RegExp, range: RegExp): readonly Question[];
|
|
1175
|
+
/** The version a workspace starts at. */
|
|
1176
|
+
export declare const DEFAULT_VERSION = "0.0.1";
|
|
1231
1177
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1178
|
+
/**
|
|
1179
|
+
* Measure one declared package list against the name and range syntax it accepts.
|
|
1180
|
+
*
|
|
1181
|
+
* @param dependencies - The declared list.
|
|
1182
|
+
* @param field - The blueprint field the list came from, reported on each question.
|
|
1183
|
+
* @param name - The package-name syntax the field accepts.
|
|
1184
|
+
* @param range - The range syntax the field accepts.
|
|
1185
|
+
* @returns One blocking question per rejected name, repeated name, and rejected
|
|
1186
|
+
* range, in list order.
|
|
1187
|
+
*
|
|
1188
|
+
* @remarks
|
|
1189
|
+
* The declared lists and peer partitions differ only in the syntaxes they
|
|
1190
|
+
* accept, so the rules live here once and each caller supplies its own patterns.
|
|
1191
|
+
* A runtime dependency name reaches a path through its guide mirror and is
|
|
1192
|
+
* fixed to the `@orkestrel` scope. A foreign peer or development extra reaches
|
|
1193
|
+
* no path and admits any valid npm name.
|
|
1194
|
+
*
|
|
1195
|
+
* Both patterns must be stateless. A global or sticky pattern carries a
|
|
1196
|
+
* `lastIndex` between calls, so it would answer differently for the same input
|
|
1197
|
+
* depending on what was tested before it.
|
|
1198
|
+
*
|
|
1199
|
+
* @example
|
|
1200
|
+
* ```ts
|
|
1201
|
+
* import { DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN } from '@orkestrel/scaffold'
|
|
1202
|
+
* import { dependenciesToQuestions } from '@orkestrel/scaffold'
|
|
1203
|
+
*
|
|
1204
|
+
* dependenciesToQuestions(
|
|
1205
|
+
* [{ name: '@orkestrel/router', range: '0.0.8' }],
|
|
1206
|
+
* 'dependencies',
|
|
1207
|
+
* DEPENDENCY_NAME_PATTERN,
|
|
1208
|
+
* ORKESTREL_RANGE_PATTERN,
|
|
1209
|
+
* ).length // 1 — the range is not caret-pinned
|
|
1210
|
+
* ```
|
|
1211
|
+
*/
|
|
1212
|
+
export declare function dependenciesToQuestions(dependencies: readonly Dependency[], field: string, name: RegExp, range: RegExp): readonly Question[];
|
|
1255
1213
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1214
|
+
/**
|
|
1215
|
+
* One runtime `@orkestrel/*` dependency of a generated workspace.
|
|
1216
|
+
*
|
|
1217
|
+
* @remarks
|
|
1218
|
+
* `optional` is meaningful only on a blueprint's `peers`, where it emits a
|
|
1219
|
+
* `peerDependenciesMeta` entry beside the peer.
|
|
1220
|
+
*/
|
|
1221
|
+
export declare interface Dependency {
|
|
1222
|
+
readonly name: string;
|
|
1223
|
+
readonly range: string;
|
|
1224
|
+
readonly optional?: boolean;
|
|
1225
|
+
}
|
|
1258
1226
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* The runtime dependency name syntax: the `@orkestrel` scope and a bare name.
|
|
1229
|
+
*
|
|
1230
|
+
* @remarks
|
|
1231
|
+
* A dependency name reaches a path, because a workspace's guide mirror is
|
|
1232
|
+
* derived from it. Fixing the scope and forbidding everything but the bare name
|
|
1233
|
+
* after it is what stops a hand-built name from escaping the directory the
|
|
1234
|
+
* mirror belongs in.
|
|
1235
|
+
*/
|
|
1236
|
+
export declare const DEPENDENCY_NAME_PATTERN: RegExp;
|
|
1268
1237
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1238
|
+
/** The packed-package proof whose presence makes a workspace `distribution`. */
|
|
1239
|
+
export declare const DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
|
|
1271
1240
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1241
|
+
/**
|
|
1242
|
+
* How one target path compares to the artifact planned for it.
|
|
1243
|
+
*
|
|
1244
|
+
* @remarks
|
|
1245
|
+
* `foreign` is a path the plan does not own at all. It is also the set
|
|
1246
|
+
* `overwrite` deletes from, narrowed by the paths no verb may remove and by
|
|
1247
|
+
* what git tracks.
|
|
1248
|
+
*/
|
|
1249
|
+
export declare type Drift = 'aligned' | 'stale' | 'missing' | 'foreign';
|
|
1274
1250
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
*
|
|
1278
|
-
* @remarks
|
|
1279
|
-
* A blueprint's `src` and `app` axes are caller-supplied, so the gate measures
|
|
1280
|
-
* each selection against this list and reports it as the accepted candidates
|
|
1281
|
-
* when it rejects one. It is also the key order the per-environment matrices
|
|
1282
|
-
* are read in.
|
|
1283
|
-
*/
|
|
1284
|
-
export declare const ENVIRONMENTS: readonly Environment[];
|
|
1251
|
+
/** The minimum-Node engine syntax a blueprint declares. */
|
|
1252
|
+
export declare const ENGINES_PATTERN: RegExp;
|
|
1285
1253
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
*
|
|
1289
|
-
* @remarks
|
|
1290
|
-
* Declared rather than read from the staging host's filesystem, because that
|
|
1291
|
-
* reading is not portable: Windows carries no executable bit, so a host staged
|
|
1292
|
-
* there reports every file non-executable and every target receives hooks it
|
|
1293
|
-
* cannot run. Declaring the set here makes one checkout stage one manifest on
|
|
1294
|
-
* every host.
|
|
1295
|
-
*
|
|
1296
|
-
* Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
|
|
1297
|
-
* that must run when a target invokes it belongs here the moment it is vendored.
|
|
1298
|
-
*/
|
|
1299
|
-
export declare const EXECUTABLE_PATHS: readonly string[];
|
|
1254
|
+
/** One environment a generated workspace selects on its `src` or `app` axis. */
|
|
1255
|
+
export declare type Environment = 'core' | 'browser' | 'server';
|
|
1300
1256
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1257
|
+
/**
|
|
1258
|
+
* The `Environment` values, frozen.
|
|
1259
|
+
*
|
|
1260
|
+
* @remarks
|
|
1261
|
+
* A blueprint's `src` and `app` axes are caller-supplied, so the gate measures
|
|
1262
|
+
* each selection against this list and reports it as the accepted candidates
|
|
1263
|
+
* when it rejects one. It is also the key order the per-environment matrices
|
|
1264
|
+
* are read in.
|
|
1265
|
+
*/
|
|
1266
|
+
export declare const ENVIRONMENTS: readonly Environment[];
|
|
1303
1267
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
* import { extractVersion } from '@orkestrel/scaffold'
|
|
1319
|
-
*
|
|
1320
|
-
* extractVersion('0.0.23') // [0, 0, 23]
|
|
1321
|
-
* extractVersion('1.2.3-beta.1') // undefined
|
|
1322
|
-
* ```
|
|
1323
|
-
*/
|
|
1324
|
-
export declare function extractVersion(version: string): readonly [major: number, minor: number, patch: number] | undefined;
|
|
1268
|
+
/**
|
|
1269
|
+
* The vendored paths a target receives with its executable bit set, frozen.
|
|
1270
|
+
*
|
|
1271
|
+
* @remarks
|
|
1272
|
+
* Declared rather than read from the staging host's filesystem, because that
|
|
1273
|
+
* reading is not portable: Windows carries no executable bit, so a host staged
|
|
1274
|
+
* there reports every file non-executable and every target receives hooks it
|
|
1275
|
+
* cannot run. Declaring the set here makes one checkout stage one manifest on
|
|
1276
|
+
* every host.
|
|
1277
|
+
*
|
|
1278
|
+
* Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
|
|
1279
|
+
* that must run when a target invokes it belongs here the moment it is vendored.
|
|
1280
|
+
*/
|
|
1281
|
+
export declare const EXECUTABLE_PATHS: readonly string[];
|
|
1325
1282
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
*
|
|
1329
|
-
* @remarks
|
|
1330
|
-
* `observed` carries the destination's exact bytes and is the precondition the
|
|
1331
|
-
* mutation is held to: a write that replaces stale bytes and a deletion that
|
|
1332
|
-
* removes a foreign file each fail when the destination no longer matches what
|
|
1333
|
-
* the finding recorded. Both therefore require it, and the requirement is in
|
|
1334
|
-
* the type rather than in prose, because a deletion that cannot bind to what
|
|
1335
|
-
* the audit showed is the one thing the destructive verb must never do. A
|
|
1336
|
-
* missing destination has no bytes to record. An aligned one may have gone
|
|
1337
|
-
* uncompared, which is what a birth-owned path always does, so it records
|
|
1338
|
-
* bytes only where they were actually read. Every planned finding carries its
|
|
1339
|
-
* artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
|
|
1340
|
-
* path from the finding itself. A foreign finding has no planned artifact and
|
|
1341
|
-
* therefore no ownership.
|
|
1342
|
-
*
|
|
1343
|
-
* Which combinations of `ownership`, `drift`, and `observed` a real audit
|
|
1344
|
-
* produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
|
|
1345
|
-
* never compared and is always aligned, a presence-owned path compares existence
|
|
1346
|
-
* only, and bytes are recorded only where they were read. This shape therefore
|
|
1347
|
-
* admits a combination that law never produces — a birth-owned path reported
|
|
1348
|
-
* stale is the plainest one — and it admits it deliberately: restating the
|
|
1349
|
-
* comparison's case analysis here would be a second copy of it, able to disagree
|
|
1350
|
-
* with the one that decides. What closes the gap is the writer: `repair` and
|
|
1351
|
-
* `remove` re-derive every verdict themselves and refuse a caller's audit that
|
|
1352
|
-
* does not match, so a verdict the comparison could not have reached is refused
|
|
1353
|
-
* rather than acted on.
|
|
1354
|
-
*/
|
|
1355
|
-
export declare type Finding = {
|
|
1356
|
-
readonly path: string;
|
|
1357
|
-
readonly group: Group;
|
|
1358
|
-
readonly ownership: Ownership;
|
|
1359
|
-
readonly drift: 'stale';
|
|
1360
|
-
readonly observed: string;
|
|
1361
|
-
} | {
|
|
1362
|
-
readonly path: string;
|
|
1363
|
-
readonly group: Group;
|
|
1364
|
-
readonly ownership?: never;
|
|
1365
|
-
readonly drift: 'foreign';
|
|
1366
|
-
readonly observed: string;
|
|
1367
|
-
} | {
|
|
1368
|
-
readonly path: string;
|
|
1369
|
-
readonly group: Group;
|
|
1370
|
-
readonly ownership: Ownership;
|
|
1371
|
-
readonly drift: 'missing';
|
|
1372
|
-
readonly observed?: never;
|
|
1373
|
-
} | {
|
|
1374
|
-
readonly path: string;
|
|
1375
|
-
readonly group: Group;
|
|
1376
|
-
readonly ownership: Ownership;
|
|
1377
|
-
readonly drift: 'aligned';
|
|
1378
|
-
readonly observed?: string;
|
|
1379
|
-
};
|
|
1380
|
-
|
|
1381
|
-
/**
|
|
1382
|
-
* The exact three-component floor accepted for a foreign peer's range.
|
|
1383
|
-
*
|
|
1384
|
-
* @remarks
|
|
1385
|
-
* This is independent from {@link ENGINES_PATTERN}. An engine floors the Node
|
|
1386
|
-
* runtime a workspace supports, while a peer floors a tool the consumer
|
|
1387
|
-
* supplies. Either obligation may change without changing the other.
|
|
1388
|
-
*/
|
|
1389
|
-
export declare const FLOOR_RANGE_PATTERN: RegExp;
|
|
1283
|
+
/** The registry-only semver subset accepted for a development extra's range. */
|
|
1284
|
+
export declare const EXTRA_RANGE_PATTERN: RegExp;
|
|
1390
1285
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1286
|
+
/**
|
|
1287
|
+
* Extract the major, minor, and patch components of an exact version.
|
|
1288
|
+
*
|
|
1289
|
+
* @param version - The candidate version text.
|
|
1290
|
+
* @returns The major, minor, and patch numbers, or `undefined` when the text is
|
|
1291
|
+
* not the exact `major.minor.patch` syntax.
|
|
1292
|
+
*
|
|
1293
|
+
* @remarks
|
|
1294
|
+
* Deliberately narrow: a prerelease or build suffix is not extracted, because
|
|
1295
|
+
* this package compares released versions and refusing to read one is honest
|
|
1296
|
+
* where guessing its precedence would not be.
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```ts
|
|
1300
|
+
* import { extractVersion } from '@orkestrel/scaffold'
|
|
1301
|
+
*
|
|
1302
|
+
* extractVersion('0.0.23') // [0, 0, 23]
|
|
1303
|
+
* extractVersion('1.2.3-beta.1') // undefined
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
export declare function extractVersion(version: string): readonly [major: number, minor: number, patch: number] | undefined;
|
|
1402
1307
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* One drift verdict against a target path.
|
|
1310
|
+
*
|
|
1311
|
+
* @remarks
|
|
1312
|
+
* `observed` carries the destination's exact bytes and is the precondition the
|
|
1313
|
+
* mutation is held to: a write that replaces stale bytes and a deletion that
|
|
1314
|
+
* removes a foreign file each fail when the destination no longer matches what
|
|
1315
|
+
* the finding recorded. Both therefore require it, and the requirement is in
|
|
1316
|
+
* the type rather than in prose, because a deletion that cannot bind to what
|
|
1317
|
+
* the audit showed is the one thing the destructive verb must never do. A
|
|
1318
|
+
* missing destination has no bytes to record. An aligned one may have gone
|
|
1319
|
+
* uncompared, which is what a birth-owned path always does, so it records
|
|
1320
|
+
* bytes only where they were actually read. Every planned finding carries its
|
|
1321
|
+
* artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
|
|
1322
|
+
* path from the finding itself. A foreign finding has no planned artifact and
|
|
1323
|
+
* therefore no ownership.
|
|
1324
|
+
*
|
|
1325
|
+
* Which combinations of `ownership`, `drift`, and `observed` a real audit
|
|
1326
|
+
* produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
|
|
1327
|
+
* never compared and is always aligned, a presence-owned path compares existence
|
|
1328
|
+
* only, and bytes are recorded only where they were read. This shape therefore
|
|
1329
|
+
* admits a combination that law never produces — a birth-owned path reported
|
|
1330
|
+
* stale is the plainest one — and it admits it deliberately: restating the
|
|
1331
|
+
* comparison's case analysis here would be a second copy of it, able to disagree
|
|
1332
|
+
* with the one that decides. What closes the gap is the writer: `repair` and
|
|
1333
|
+
* `remove` re-derive every verdict themselves and refuse a caller's audit that
|
|
1334
|
+
* does not match, so a verdict the comparison could not have reached is refused
|
|
1335
|
+
* rather than acted on.
|
|
1336
|
+
*/
|
|
1337
|
+
export declare type Finding = {
|
|
1338
|
+
readonly path: string;
|
|
1339
|
+
readonly group: Group;
|
|
1340
|
+
readonly ownership: Ownership;
|
|
1341
|
+
readonly drift: 'stale';
|
|
1342
|
+
readonly observed: string;
|
|
1343
|
+
} | {
|
|
1344
|
+
readonly path: string;
|
|
1345
|
+
readonly group: Group;
|
|
1346
|
+
readonly ownership?: never;
|
|
1347
|
+
readonly drift: 'foreign';
|
|
1348
|
+
readonly observed: string;
|
|
1349
|
+
} | {
|
|
1350
|
+
readonly path: string;
|
|
1351
|
+
readonly group: Group;
|
|
1352
|
+
readonly ownership: Ownership;
|
|
1353
|
+
readonly drift: 'missing';
|
|
1354
|
+
readonly observed?: never;
|
|
1355
|
+
} | {
|
|
1356
|
+
readonly path: string;
|
|
1357
|
+
readonly group: Group;
|
|
1358
|
+
readonly ownership: Ownership;
|
|
1359
|
+
readonly drift: 'aligned';
|
|
1360
|
+
readonly observed?: string;
|
|
1361
|
+
};
|
|
1405
1362
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1363
|
+
/**
|
|
1364
|
+
* The exact `major.minor.patch` floor accepted for a foreign peer's range.
|
|
1365
|
+
*
|
|
1366
|
+
* @remarks
|
|
1367
|
+
* This is independent from {@link ENGINES_PATTERN}. An engine floors the Node
|
|
1368
|
+
* runtime a workspace supports, while a peer floors a tool the consumer
|
|
1369
|
+
* supplies. Either obligation may change without changing the other.
|
|
1370
|
+
*/
|
|
1371
|
+
export declare const FLOOR_RANGE_PATTERN: RegExp;
|
|
1408
1372
|
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1373
|
+
/**
|
|
1374
|
+
* The package name syntax for a dependency this package does not publish.
|
|
1375
|
+
*
|
|
1376
|
+
* @remarks
|
|
1377
|
+
* A foreign package is one this package does not publish, so its name reaches
|
|
1378
|
+
* no path. It may carry any scope or no scope at all. Each name segment begins
|
|
1379
|
+
* with an alphanumeric character after an optional leading `@`, and the name
|
|
1380
|
+
* carries at most one `/`. No segment can therefore be `..`, and no backslash
|
|
1381
|
+
* is admitted, so the shape cannot express a traversal.
|
|
1382
|
+
*/
|
|
1383
|
+
export declare const FOREIGN_NAME_PATTERN: RegExp;
|
|
1418
1384
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1385
|
+
/** The shared Vitest global-setup module whose presence makes a workspace `global`. */
|
|
1386
|
+
export declare const GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
|
|
1421
1387
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1388
|
+
/** The artifact group a plan selects over. */
|
|
1389
|
+
export declare type Group = 'manifest' | 'configs' | 'source' | 'tests' | 'guides' | 'docs' | 'orchestration';
|
|
1424
1390
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
* everything beneath it.
|
|
1435
|
-
*
|
|
1436
|
-
* A plan carries the subset its target selects, which is why the list is a
|
|
1437
|
-
* candidate set rather than a plan: a workspace never mirrors its own guide.
|
|
1438
|
-
*/
|
|
1439
|
-
export declare const HOST_PATHS: readonly string[];
|
|
1391
|
+
/**
|
|
1392
|
+
* The `Group` values in plan order, frozen.
|
|
1393
|
+
*
|
|
1394
|
+
* @remarks
|
|
1395
|
+
* A compile that names no groups covers every one of them, so this list is the
|
|
1396
|
+
* default selection as well as the accepted candidates for a rejected one. The
|
|
1397
|
+
* order is the order a plan lists its artifacts in.
|
|
1398
|
+
*/
|
|
1399
|
+
export declare const GROUPS: readonly Group[];
|
|
1440
1400
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
*
|
|
1444
|
-
* @remarks
|
|
1445
|
-
* `source` falls back to `path` when absent. The pure core face cannot read the
|
|
1446
|
-
* vendored root, so a plan it compiles alone claims only that these files
|
|
1447
|
-
* exist. That is why the ownership here is narrowed away from `content`: a
|
|
1448
|
-
* claim over bytes nobody has read is a claim that cannot be checked. Reading
|
|
1449
|
-
* the vendored root turns the ones scaffold owns the bytes of into
|
|
1450
|
-
* {@link HydratedArtifact}. Workspace-owned paths and paths whose bytes belong
|
|
1451
|
-
* to another verb stay plain host artifacts, because this writer claims only
|
|
1452
|
-
* their presence.
|
|
1453
|
-
*/
|
|
1454
|
-
export declare interface HostArtifact extends ArtifactBase {
|
|
1455
|
-
readonly origin: 'host';
|
|
1456
|
-
readonly ownership: 'presence' | 'birth';
|
|
1457
|
-
readonly source?: string;
|
|
1458
|
-
readonly hex?: never;
|
|
1459
|
-
readonly content?: never;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
/**
|
|
1463
|
-
* A vendored file whose exact bytes have been read, so its content can be compared.
|
|
1464
|
-
*
|
|
1465
|
-
* @remarks
|
|
1466
|
-
* `hex` is the canonical lowercase byte pairs of the vendored source. It is
|
|
1467
|
-
* required, which is what makes content ownership honest: every artifact
|
|
1468
|
-
* claiming a byte comparison carries the bytes that comparison needs. Hydration
|
|
1469
|
-
* leaves a guide-mirror pointer and the catalog agent as plain host artifacts,
|
|
1470
|
-
* because another verb owns those bytes.
|
|
1471
|
-
*/
|
|
1472
|
-
export declare interface HydratedArtifact extends ArtifactBase {
|
|
1473
|
-
readonly origin: 'host';
|
|
1474
|
-
readonly ownership: 'content';
|
|
1475
|
-
readonly source?: string;
|
|
1476
|
-
readonly hex: string;
|
|
1477
|
-
readonly content?: never;
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
/**
|
|
1481
|
-
* Infer how one target path compares to the artifact planned for it.
|
|
1482
|
-
*
|
|
1483
|
-
* @param artifact - The planned artifact.
|
|
1484
|
-
* @param observed - The destination's exact bytes as hexadecimal; absent when
|
|
1485
|
-
* the destination holds no file.
|
|
1486
|
-
* @returns `aligned`, `stale`, or `missing`.
|
|
1487
|
-
*
|
|
1488
|
-
* @remarks
|
|
1489
|
-
* Ownership decides the comparison and nothing else does. A `birth`-owned
|
|
1490
|
-
* artifact is never compared and is always aligned, so a file the workspace has
|
|
1491
|
-
* outgrown is never reported as drift. A `presence`-owned artifact compares
|
|
1492
|
-
* existence only. A `content`-owned artifact compares bytes, and it always
|
|
1493
|
-
* carries the bytes to compare, so the comparison can always be made.
|
|
1494
|
-
*
|
|
1495
|
-
* `foreign` is not answerable here: it describes a path the plan does not own,
|
|
1496
|
-
* so no artifact exists to pass in.
|
|
1497
|
-
*
|
|
1498
|
-
* @example
|
|
1499
|
-
* ```ts
|
|
1500
|
-
* import type { Artifact } from '@orkestrel/scaffold'
|
|
1501
|
-
* import { inferDrift } from '@orkestrel/scaffold'
|
|
1502
|
-
*
|
|
1503
|
-
* const artifact: Artifact = {
|
|
1504
|
-
* path: 'README.md',
|
|
1505
|
-
* group: 'docs',
|
|
1506
|
-
* ownership: 'content',
|
|
1507
|
-
* origin: 'computed',
|
|
1508
|
-
* content: 'hi\n',
|
|
1509
|
-
* }
|
|
1510
|
-
*
|
|
1511
|
-
* inferDrift(artifact, '68690a') // 'aligned'
|
|
1512
|
-
* inferDrift(artifact, '6279650a') // 'stale'
|
|
1513
|
-
* inferDrift(artifact) // 'missing'
|
|
1514
|
-
* ```
|
|
1515
|
-
*/
|
|
1516
|
-
export declare function inferDrift(artifact: Artifact, observed?: string): Exclude<Drift, 'foreign'>;
|
|
1401
|
+
/** The guide-parity proof whose presence selects the planned `guides` project. */
|
|
1402
|
+
export declare const GUIDES_TEST_PATH = "tests/guides.test.ts";
|
|
1517
1403
|
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
*
|
|
1521
|
-
* @param path - The target-relative path to classify.
|
|
1522
|
-
* @returns The group that owns the path.
|
|
1523
|
-
*
|
|
1524
|
-
* @remarks
|
|
1525
|
-
* A path is grouped by what it governs rather than by where it sits. The two
|
|
1526
|
-
* manifest files are named exactly; anything
|
|
1527
|
-
* {@link matchesOrchestrationPath} accepts is orchestration; `src` and `app`
|
|
1528
|
-
* are source; `tests`, `guides`, and `docs` carry their own names; the licence
|
|
1529
|
-
* and a root Markdown document are docs; and everything else is configuration.
|
|
1530
|
-
* A vendored path and a foreign path found in a target are classified here
|
|
1531
|
-
* alike, so the plan and the audit never disagree about what a path is.
|
|
1532
|
-
*
|
|
1533
|
-
* @example
|
|
1534
|
-
* ```ts
|
|
1535
|
-
* import { inferGroup } from '@orkestrel/scaffold'
|
|
1536
|
-
*
|
|
1537
|
-
* inferGroup('src/core/index.ts') // 'source'
|
|
1538
|
-
* inferGroup('AGENTS.md') // 'docs'
|
|
1539
|
-
* inferGroup('.editorconfig') // 'configs'
|
|
1540
|
-
* ```
|
|
1541
|
-
*/
|
|
1542
|
-
export declare function inferGroup(path: string): Group;
|
|
1404
|
+
/** Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. */
|
|
1405
|
+
export declare const HEX_PATTERN: RegExp;
|
|
1543
1406
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1407
|
+
/**
|
|
1408
|
+
* The paths byte-copied from the vendored data root, frozen.
|
|
1409
|
+
*
|
|
1410
|
+
* @remarks
|
|
1411
|
+
* These are the files the fleet shares verbatim: the root instruction
|
|
1412
|
+
* documents, the licence, the canonical orchestration contract every harness
|
|
1413
|
+
* bridge points at, the harness directories, the session hook scripts,
|
|
1414
|
+
* the shared policy register, the byte-identical root dotfiles, and the
|
|
1415
|
+
* guide mirrors a generated workspace starts from. A directory entry vendors
|
|
1416
|
+
* everything beneath it.
|
|
1417
|
+
*
|
|
1418
|
+
* A plan carries the subset its target selects, which is why the list is a
|
|
1419
|
+
* candidate set rather than a plan: a workspace never mirrors its own guide.
|
|
1420
|
+
*/
|
|
1421
|
+
export declare const HOST_PATHS: readonly string[];
|
|
1546
1422
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1423
|
+
/**
|
|
1424
|
+
* A file byte-copied from the vendored data root, planned before its bytes are read.
|
|
1425
|
+
*
|
|
1426
|
+
* @remarks
|
|
1427
|
+
* `source` falls back to `path` when absent. The pure core face cannot read the
|
|
1428
|
+
* vendored root, so a plan it compiles alone claims only that these files
|
|
1429
|
+
* exist. That is why the ownership here is narrowed away from `content`: a
|
|
1430
|
+
* claim over bytes nobody has read is a claim that cannot be checked. Reading
|
|
1431
|
+
* the vendored root turns the ones scaffold owns the bytes of into
|
|
1432
|
+
* {@link HydratedArtifact}. Workspace-owned paths and paths whose bytes belong
|
|
1433
|
+
* to another verb stay plain host artifacts, because this writer claims only
|
|
1434
|
+
* their presence.
|
|
1435
|
+
*/
|
|
1436
|
+
export declare interface HostArtifact extends ArtifactBase {
|
|
1437
|
+
readonly origin: 'host';
|
|
1438
|
+
readonly ownership: 'presence' | 'birth';
|
|
1439
|
+
readonly source?: string;
|
|
1440
|
+
readonly hex?: never;
|
|
1441
|
+
readonly content?: never;
|
|
1442
|
+
}
|
|
1549
1443
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
export declare const isArtifact: Guard<Artifact>;
|
|
1444
|
+
/**
|
|
1445
|
+
* A vendored file whose exact bytes have been read, so its content can be compared.
|
|
1446
|
+
*
|
|
1447
|
+
* @remarks
|
|
1448
|
+
* `hex` is the canonical lowercase byte pairs of the vendored source. It is
|
|
1449
|
+
* required, which is what makes content ownership honest: every artifact
|
|
1450
|
+
* claiming a byte comparison carries the bytes that comparison needs. Hydration
|
|
1451
|
+
* leaves a guide-mirror pointer and the catalog agent as plain host artifacts,
|
|
1452
|
+
* because another verb owns those bytes.
|
|
1453
|
+
*/
|
|
1454
|
+
export declare interface HydratedArtifact extends ArtifactBase {
|
|
1455
|
+
readonly origin: 'host';
|
|
1456
|
+
readonly ownership: 'content';
|
|
1457
|
+
readonly source?: string;
|
|
1458
|
+
readonly hex: string;
|
|
1459
|
+
readonly content?: never;
|
|
1460
|
+
}
|
|
1568
1461
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1462
|
+
/**
|
|
1463
|
+
* Infer how one target path compares to the artifact planned for it.
|
|
1464
|
+
*
|
|
1465
|
+
* @param artifact - The planned artifact.
|
|
1466
|
+
* @param observed - The destination's exact bytes as hexadecimal; absent when
|
|
1467
|
+
* the destination holds no file.
|
|
1468
|
+
* @returns `aligned`, `stale`, or `missing`.
|
|
1469
|
+
*
|
|
1470
|
+
* @remarks
|
|
1471
|
+
* Ownership decides the comparison and nothing else does. A `birth`-owned
|
|
1472
|
+
* artifact is never compared and is always aligned, so a file the workspace has
|
|
1473
|
+
* outgrown is never reported as drift. A `presence`-owned artifact compares
|
|
1474
|
+
* existence only. A `content`-owned artifact compares bytes, and it always
|
|
1475
|
+
* carries the bytes to compare, so the comparison can always be made.
|
|
1476
|
+
*
|
|
1477
|
+
* `foreign` is not answerable here: it describes a path the plan does not own,
|
|
1478
|
+
* so no artifact exists to pass in.
|
|
1479
|
+
*
|
|
1480
|
+
* @example
|
|
1481
|
+
* ```ts
|
|
1482
|
+
* import type { Artifact } from '@orkestrel/scaffold'
|
|
1483
|
+
* import { inferDrift } from '@orkestrel/scaffold'
|
|
1484
|
+
*
|
|
1485
|
+
* const artifact: Artifact = {
|
|
1486
|
+
* path: 'README.md',
|
|
1487
|
+
* group: 'docs',
|
|
1488
|
+
* ownership: 'content',
|
|
1489
|
+
* origin: 'computed',
|
|
1490
|
+
* content: 'hi\n',
|
|
1491
|
+
* }
|
|
1492
|
+
*
|
|
1493
|
+
* inferDrift(artifact, '68690a') // 'aligned'
|
|
1494
|
+
* inferDrift(artifact, '6279650a') // 'stale'
|
|
1495
|
+
* inferDrift(artifact) // 'missing'
|
|
1496
|
+
* ```
|
|
1497
|
+
*/
|
|
1498
|
+
export declare function inferDrift(artifact: Artifact, observed?: string): Exclude<Drift, 'foreign'>;
|
|
1578
1499
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1500
|
+
/**
|
|
1501
|
+
* Infer the {@link Group} a path belongs to.
|
|
1502
|
+
*
|
|
1503
|
+
* @param path - The target-relative path to classify.
|
|
1504
|
+
* @returns The group that owns the path.
|
|
1505
|
+
*
|
|
1506
|
+
* @remarks
|
|
1507
|
+
* A path is grouped by what it governs rather than by where it sits. The
|
|
1508
|
+
* manifest files are named exactly; anything
|
|
1509
|
+
* {@link matchesOrchestrationPath} accepts is orchestration; `src` and `app`
|
|
1510
|
+
* are source; `tests`, `guides`, and `docs` carry their own names; the licence
|
|
1511
|
+
* and a root Markdown document are docs; and everything else is configuration.
|
|
1512
|
+
* A vendored path and a foreign path found in a target are classified here
|
|
1513
|
+
* alike, so the plan and the audit never disagree about what a path is.
|
|
1514
|
+
*
|
|
1515
|
+
* @example
|
|
1516
|
+
* ```ts
|
|
1517
|
+
* import { inferGroup } from '@orkestrel/scaffold'
|
|
1518
|
+
*
|
|
1519
|
+
* inferGroup('src/core/index.ts') // 'source'
|
|
1520
|
+
* inferGroup('AGENTS.md') // 'docs'
|
|
1521
|
+
* inferGroup('.editorconfig') // 'configs'
|
|
1522
|
+
* ```
|
|
1523
|
+
*/
|
|
1524
|
+
export declare function inferGroup(path: string): Group;
|
|
1596
1525
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
*
|
|
1600
|
-
* @remarks
|
|
1601
|
-
* A row that found no version carries the cause instead, and neither branch may
|
|
1602
|
-
* carry the other's field.
|
|
1603
|
-
*/
|
|
1604
|
-
export declare const isCatalogEntry: Guard<CatalogEntry>;
|
|
1526
|
+
/** The cross-environment composition proof whose presence makes a workspace `integration`. */
|
|
1527
|
+
export declare const INTEGRATION_TEST_PATH = "tests/integration.test.ts";
|
|
1605
1528
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
*
|
|
1609
|
-
* @param value - The candidate collection.
|
|
1610
|
-
* @returns `true` for an array of no more than `MAX_COLLECTION_ITEMS` items.
|
|
1611
|
-
*
|
|
1612
|
-
* @remarks
|
|
1613
|
-
* Compose this ahead of an element guard so the item count is settled before
|
|
1614
|
-
* anything walks the items. A hostile `length` accessor answers `false` here
|
|
1615
|
-
* rather than escaping as a thrown error.
|
|
1616
|
-
*
|
|
1617
|
-
* @example
|
|
1618
|
-
* ```ts
|
|
1619
|
-
* import { isCollection } from '@orkestrel/scaffold'
|
|
1620
|
-
*
|
|
1621
|
-
* isCollection(['manifest']) // true
|
|
1622
|
-
* isCollection('manifest') // false
|
|
1623
|
-
* ```
|
|
1624
|
-
*/
|
|
1625
|
-
export declare function isCollection(value: unknown): value is readonly unknown[];
|
|
1529
|
+
/** Visible characters a target-relative path and a Markdown path cell both forbid. */
|
|
1530
|
+
export declare const INVALID_PATH_CHARACTER_PATTERN: RegExp;
|
|
1626
1531
|
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1532
|
+
/**
|
|
1533
|
+
* Narrow a value to an {@link Artifact}.
|
|
1534
|
+
*
|
|
1535
|
+
* @remarks
|
|
1536
|
+
* One branch per way content is produced, discriminated by `origin` and
|
|
1537
|
+
* narrowed by `ownership`. Each branch declares only the keys its branch has,
|
|
1538
|
+
* so a host artifact carrying `content`, a hydrated artifact carrying anything
|
|
1539
|
+
* but `content` ownership, and a template artifact carrying `hex` are all
|
|
1540
|
+
* refused rather than admitted to the wrong branch.
|
|
1541
|
+
*
|
|
1542
|
+
* @example
|
|
1543
|
+
* ```ts
|
|
1544
|
+
* import { isArtifact } from '@orkestrel/scaffold'
|
|
1545
|
+
*
|
|
1546
|
+
* isArtifact({ path: 'AGENTS.md', group: 'docs', ownership: 'presence', origin: 'host' }) // true
|
|
1547
|
+
* ```
|
|
1548
|
+
*/
|
|
1549
|
+
export declare const isArtifact: Guard<Artifact>;
|
|
1636
1550
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
* ```
|
|
1647
|
-
*/
|
|
1648
|
-
export declare const isCompilerOptions: Guard<CompilerOptions>;
|
|
1551
|
+
/**
|
|
1552
|
+
* Narrow a value to an {@link Audit}.
|
|
1553
|
+
*
|
|
1554
|
+
* @remarks
|
|
1555
|
+
* An audit reaches the writer and the destructive verb, so it is guarded as
|
|
1556
|
+
* strictly as the plan beside it. Findings use the sum of the producer
|
|
1557
|
+
* bounds: one per planned artifact, then one per unplanned snapshot path.
|
|
1558
|
+
*/
|
|
1559
|
+
export declare const isAudit: Guard<Audit>;
|
|
1649
1560
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1561
|
+
/**
|
|
1562
|
+
* Narrow a value to a {@link Blueprint}.
|
|
1563
|
+
*
|
|
1564
|
+
* @remarks
|
|
1565
|
+
* The whole closed record, its literal axes, and the count and length bounds
|
|
1566
|
+
* this package admits. The syntactic laws over a name, a version, a range, and
|
|
1567
|
+
* an engines floor stay with the gate, which reports each one as a
|
|
1568
|
+
* {@link Question} instead of refusing the value outright.
|
|
1569
|
+
*
|
|
1570
|
+
* @example
|
|
1571
|
+
* ```ts
|
|
1572
|
+
* import { isBlueprint } from '@orkestrel/scaffold'
|
|
1573
|
+
*
|
|
1574
|
+
* isBlueprint({ name: 'router', src: ['core'] }) // false — not the whole record
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1577
|
+
export declare const isBlueprint: Guard<Blueprint>;
|
|
1661
1578
|
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
* @example
|
|
1671
|
-
* ```ts
|
|
1672
|
-
* import { isDependency } from '@orkestrel/scaffold'
|
|
1673
|
-
*
|
|
1674
|
-
* isDependency({ name: '@orkestrel/emitter', range: '^0.0.5' }) // true
|
|
1675
|
-
* isDependency({ name: '@orkestrel/emitter' }) // false
|
|
1676
|
-
* ```
|
|
1677
|
-
*/
|
|
1678
|
-
export declare const isDependency: Guard<Dependency>;
|
|
1579
|
+
/**
|
|
1580
|
+
* Narrow a value to a {@link CatalogEntry}.
|
|
1581
|
+
*
|
|
1582
|
+
* @remarks
|
|
1583
|
+
* A row that found no version carries the cause instead, and neither branch may
|
|
1584
|
+
* carry the other's field.
|
|
1585
|
+
*/
|
|
1586
|
+
export declare const isCatalogEntry: Guard<CatalogEntry>;
|
|
1679
1587
|
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1588
|
+
/**
|
|
1589
|
+
* Narrow a value to an array within the limit one public collection accepts.
|
|
1590
|
+
*
|
|
1591
|
+
* @param value - The candidate collection.
|
|
1592
|
+
* @returns `true` for an array of no more than `MAX_COLLECTION_ITEMS` items.
|
|
1593
|
+
*
|
|
1594
|
+
* @remarks
|
|
1595
|
+
* Compose this ahead of an element guard so the item count is settled before
|
|
1596
|
+
* anything walks the items. A hostile `length` accessor answers `false` here
|
|
1597
|
+
* rather than escaping as a thrown error.
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```ts
|
|
1601
|
+
* import { isCollection } from '@orkestrel/scaffold'
|
|
1602
|
+
*
|
|
1603
|
+
* isCollection(['manifest']) // true
|
|
1604
|
+
* isCollection('manifest') // false
|
|
1605
|
+
* ```
|
|
1606
|
+
*/
|
|
1607
|
+
export declare function isCollection(value: unknown): value is readonly unknown[];
|
|
1699
1608
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
* ```
|
|
1710
|
-
*/
|
|
1711
|
-
export declare const isEnvironment: Guard<Environment>;
|
|
1609
|
+
/**
|
|
1610
|
+
* Narrow a value to the compiler's initial listener record.
|
|
1611
|
+
*
|
|
1612
|
+
* @remarks
|
|
1613
|
+
* Every event is optional and every declared value is a function. A key outside
|
|
1614
|
+
* the compiler's event map is refused, so a listener wired to a misspelled
|
|
1615
|
+
* event fails at construction instead of never firing.
|
|
1616
|
+
*/
|
|
1617
|
+
export declare const isCompilerHooks: Guard<EmitterHooks<CompilerEventMap>>;
|
|
1712
1618
|
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
* {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
|
|
1726
|
-
*/
|
|
1727
|
-
export declare const isFinding: Guard<Finding>;
|
|
1619
|
+
/**
|
|
1620
|
+
* Narrow a value to {@link CompilerOptions}.
|
|
1621
|
+
*
|
|
1622
|
+
* @example
|
|
1623
|
+
* ```ts
|
|
1624
|
+
* import { isCompilerOptions } from '@orkestrel/scaffold'
|
|
1625
|
+
*
|
|
1626
|
+
* isCompilerOptions({}) // true
|
|
1627
|
+
* isCompilerOptions({ retries: 2 }) // false
|
|
1628
|
+
* ```
|
|
1629
|
+
*/
|
|
1630
|
+
export declare const isCompilerOptions: Guard<CompilerOptions>;
|
|
1728
1631
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
export declare const isGroup: Guard<Group>;
|
|
1632
|
+
/**
|
|
1633
|
+
* Narrow a value to text this package will accept as one artifact's content.
|
|
1634
|
+
*
|
|
1635
|
+
* @remarks
|
|
1636
|
+
* The bound is a code-unit ceiling rather than a byte count, because a string
|
|
1637
|
+
* of more code units than {@link MAX_ARTIFACT_BYTES} cannot encode within that
|
|
1638
|
+
* budget under any encoding this package writes. The exact UTF-8 measurement
|
|
1639
|
+
* belongs to the compiler and the writer, which are the places the bytes
|
|
1640
|
+
* are actually produced.
|
|
1641
|
+
*/
|
|
1642
|
+
export declare const isContent: Guard<string>;
|
|
1741
1643
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1644
|
+
/**
|
|
1645
|
+
* Narrow a value to a {@link Dependency}.
|
|
1646
|
+
*
|
|
1647
|
+
* @remarks
|
|
1648
|
+
* Structural and bounded: which names and ranges a blueprint may declare is a
|
|
1649
|
+
* gate law, reported as a {@link Question} carrying its accepted candidates, so
|
|
1650
|
+
* refusing it here would replace an answerable question with a bare `false`.
|
|
1651
|
+
*
|
|
1652
|
+
* @example
|
|
1653
|
+
* ```ts
|
|
1654
|
+
* import { isDependency } from '@orkestrel/scaffold'
|
|
1655
|
+
*
|
|
1656
|
+
* isDependency({ name: '@orkestrel/emitter', range: '^0.0.5' }) // true
|
|
1657
|
+
* isDependency({ name: '@orkestrel/emitter' }) // false
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1660
|
+
export declare const isDependency: Guard<Dependency>;
|
|
1744
1661
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1662
|
+
/**
|
|
1663
|
+
* Narrow a value to the scoped package name a runtime dependency carries.
|
|
1664
|
+
*
|
|
1665
|
+
* @remarks
|
|
1666
|
+
* A dependency name reaches a path, because a workspace's guide mirror is
|
|
1667
|
+
* derived from it. Fixing the scope and admitting nothing but a bare name after
|
|
1668
|
+
* it is what keeps that derivation inside the directory the mirror belongs in.
|
|
1669
|
+
* A blueprint's development extras are deliberately wider and are measured by
|
|
1670
|
+
* the gate instead, which is why {@link isDependency} does not apply this.
|
|
1671
|
+
*
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* import { isDependencyName } from '@orkestrel/scaffold'
|
|
1675
|
+
*
|
|
1676
|
+
* isDependencyName('@orkestrel/router') // true
|
|
1677
|
+
* isDependencyName('@orkestrel/../etc') // false
|
|
1678
|
+
* ```
|
|
1679
|
+
*/
|
|
1680
|
+
export declare const isDependencyName: Guard<string>;
|
|
1762
1681
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1682
|
+
/**
|
|
1683
|
+
* Narrow a value to one {@link Environment} a workspace may select.
|
|
1684
|
+
*
|
|
1685
|
+
* @example
|
|
1686
|
+
* ```ts
|
|
1687
|
+
* import { isEnvironment } from '@orkestrel/scaffold'
|
|
1688
|
+
*
|
|
1689
|
+
* isEnvironment('browser') // true
|
|
1690
|
+
* isEnvironment('worker') // false
|
|
1691
|
+
* ```
|
|
1692
|
+
*/
|
|
1693
|
+
export declare const isEnvironment: Guard<Environment>;
|
|
1772
1694
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1695
|
+
/**
|
|
1696
|
+
* Narrow a value to a {@link Finding}.
|
|
1697
|
+
*
|
|
1698
|
+
* @remarks
|
|
1699
|
+
* `observed` is required exactly where the mutation it precedes is held to it,
|
|
1700
|
+
* absent where the destination had no bytes to record, and optional where the
|
|
1701
|
+
* comparison may not have been made. Planned findings require `ownership`;
|
|
1702
|
+
* foreign findings forbid it because no artifact was planned for their path.
|
|
1703
|
+
*
|
|
1704
|
+
* That is the whole claim. This guard proves the shape a reader may destructure
|
|
1705
|
+
* and nothing about whether the verdict is one an audit could have reached: the
|
|
1706
|
+
* correlation between `ownership`, `drift`, and `observed` belongs to
|
|
1707
|
+
* {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
|
|
1708
|
+
*/
|
|
1709
|
+
export declare const isFinding: Guard<Finding>;
|
|
1788
1710
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
* target. Host-location validation is a separate server boundary: this guard
|
|
1802
|
-
* does not reject a device spelling, a trailing dot or space, or a segment that
|
|
1803
|
-
* exceeds a host filesystem's byte ceiling.
|
|
1804
|
-
*
|
|
1805
|
-
* @example
|
|
1806
|
-
* ```ts
|
|
1807
|
-
* import { isPath } from '@orkestrel/scaffold'
|
|
1808
|
-
*
|
|
1809
|
-
* isPath('configs/src/tsconfig.core.json') // true
|
|
1810
|
-
* isPath('../secrets') // false
|
|
1811
|
-
* ```
|
|
1812
|
-
*/
|
|
1813
|
-
export declare function isPath(value: unknown): value is string;
|
|
1711
|
+
/**
|
|
1712
|
+
* Narrow a value to one {@link Group} a plan selects over.
|
|
1713
|
+
*
|
|
1714
|
+
* @example
|
|
1715
|
+
* ```ts
|
|
1716
|
+
* import { isGroup } from '@orkestrel/scaffold'
|
|
1717
|
+
*
|
|
1718
|
+
* isGroup('manifest') // true
|
|
1719
|
+
* isGroup('readme') // false
|
|
1720
|
+
* ```
|
|
1721
|
+
*/
|
|
1722
|
+
export declare const isGroup: Guard<Group>;
|
|
1814
1723
|
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
*
|
|
1818
|
-
* @remarks
|
|
1819
|
-
* A plan reaches the writer, and the writer has no question channel, so this
|
|
1820
|
-
* carries the whole law of the value: every artifact path, every claimed byte,
|
|
1821
|
-
* and the blueprint it was compiled from.
|
|
1822
|
-
*/
|
|
1823
|
-
export declare const isPlan: Guard<Plan>;
|
|
1724
|
+
/** Narrow a value to a bounded group selection. */
|
|
1725
|
+
export declare const isGroups: Guard<readonly Group[]>;
|
|
1824
1726
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1727
|
+
/**
|
|
1728
|
+
* Narrow a value to exact lowercase hexadecimal bytes within one artifact's limit.
|
|
1729
|
+
*
|
|
1730
|
+
* @remarks
|
|
1731
|
+
* Two digits per byte, so an odd length is refused and empty content is valid.
|
|
1732
|
+
* The bound is exact rather than approximate: the encoding is ASCII, so the
|
|
1733
|
+
* string's length is twice the byte count it stands for.
|
|
1734
|
+
*
|
|
1735
|
+
* @example
|
|
1736
|
+
* ```ts
|
|
1737
|
+
* import { isHex } from '@orkestrel/scaffold'
|
|
1738
|
+
*
|
|
1739
|
+
* isHex('68690a') // true
|
|
1740
|
+
* isHex('68690A') // false
|
|
1741
|
+
* ```
|
|
1742
|
+
*/
|
|
1743
|
+
export declare const isHex: Guard<string>;
|
|
1836
1744
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
*
|
|
1847
|
-
* isScaffoldError(new Error('plain')) // false
|
|
1848
|
-
* isScaffoldError(undefined) // false
|
|
1849
|
-
* ```
|
|
1850
|
-
*/
|
|
1851
|
-
export declare function isScaffoldError(value: unknown): value is ScaffoldError;
|
|
1745
|
+
/**
|
|
1746
|
+
* Narrow a value to a {@link Mirror}.
|
|
1747
|
+
*
|
|
1748
|
+
* @remarks
|
|
1749
|
+
* `content` is the fetched guide text and `observed` is the local mirror's
|
|
1750
|
+
* exact bytes, so they carry different laws: one is content this package
|
|
1751
|
+
* writes, the other is the precondition that write is held to.
|
|
1752
|
+
*/
|
|
1753
|
+
export declare const isMirror: Guard<Mirror>;
|
|
1852
1754
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
* import { isSnapshot } from '@orkestrel/scaffold'
|
|
1869
|
-
*
|
|
1870
|
-
* isSnapshot({ 'AGENTS.md': '68690a' }) // true
|
|
1871
|
-
* isSnapshot({ 'AGENTS.md': 'hi' }) // false
|
|
1872
|
-
* ```
|
|
1873
|
-
*/
|
|
1874
|
-
export declare function isSnapshot(value: unknown): value is Snapshot;
|
|
1755
|
+
/**
|
|
1756
|
+
* Narrow a value to an {@link Override}.
|
|
1757
|
+
*
|
|
1758
|
+
* @remarks
|
|
1759
|
+
* Whether the path names a planned artifact is a gate law; whether it names a
|
|
1760
|
+
* destination at all is this guard's.
|
|
1761
|
+
*
|
|
1762
|
+
* @example
|
|
1763
|
+
* ```ts
|
|
1764
|
+
* import { isOverride } from '@orkestrel/scaffold'
|
|
1765
|
+
*
|
|
1766
|
+
* isOverride({ path: 'README.md', content: '# Title\n' }) // true
|
|
1767
|
+
* ```
|
|
1768
|
+
*/
|
|
1769
|
+
export declare const isOverride: Guard<Override>;
|
|
1875
1770
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1771
|
+
/**
|
|
1772
|
+
* Narrow a value to a logical target-relative path.
|
|
1773
|
+
*
|
|
1774
|
+
* @param value - The candidate path.
|
|
1775
|
+
* @returns `true` for a bounded relative path with no traversal, empty segment,
|
|
1776
|
+
* control character, or reserved syntax character.
|
|
1777
|
+
*
|
|
1778
|
+
* @remarks
|
|
1779
|
+
* Every path this package reads or writes passes here, so one law covers a
|
|
1780
|
+
* planned artifact, an override target, an audit finding, a guide mirror, and a
|
|
1781
|
+
* snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
|
|
1782
|
+
* what stops a caller-supplied path from naming a destination outside the
|
|
1783
|
+
* target. Host-location validation is a separate server boundary: this guard
|
|
1784
|
+
* does not reject a device spelling, a trailing dot or space, or a segment that
|
|
1785
|
+
* exceeds a host filesystem's byte ceiling.
|
|
1786
|
+
*
|
|
1787
|
+
* @example
|
|
1788
|
+
* ```ts
|
|
1789
|
+
* import { isPath } from '@orkestrel/scaffold'
|
|
1790
|
+
*
|
|
1791
|
+
* isPath('configs/src/tsconfig.core.json') // true
|
|
1792
|
+
* isPath('../secrets') // false
|
|
1793
|
+
* ```
|
|
1794
|
+
*/
|
|
1795
|
+
export declare function isPath(value: unknown): value is string;
|
|
1887
1796
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
* package the fleet publishes is upstream of this workspace wherever it is
|
|
1898
|
-
* declared. Every other name is skipped rather than refused: a workspace's
|
|
1899
|
-
* unrelated dependencies are not this package's to report on.
|
|
1900
|
-
*
|
|
1901
|
-
* Never throws, and every row it returns satisfies `isDependency` while the
|
|
1902
|
-
* list satisfies `isCollection`, so the result crosses the compiler's own
|
|
1903
|
-
* boundary without a second cleaning.
|
|
1904
|
-
*
|
|
1905
|
-
* @example
|
|
1906
|
-
* ```ts
|
|
1907
|
-
* import { manifestToDependencies } from '@orkestrel/scaffold'
|
|
1908
|
-
*
|
|
1909
|
-
* manifestToDependencies('{"dependencies":{"@orkestrel/emitter":"^0.0.5","vite":"~8.2.0"}}')
|
|
1910
|
-
* // [{ name: '@orkestrel/emitter', range: '^0.0.5' }]
|
|
1911
|
-
* ```
|
|
1912
|
-
*/
|
|
1913
|
-
export declare function manifestToDependencies(manifest: string): readonly Dependency[];
|
|
1797
|
+
/**
|
|
1798
|
+
* Narrow a value to a {@link Plan}.
|
|
1799
|
+
*
|
|
1800
|
+
* @remarks
|
|
1801
|
+
* A plan reaches the writer, and the writer has no question channel, so this
|
|
1802
|
+
* carries the whole law of the value: every artifact path, every claimed byte,
|
|
1803
|
+
* and the blueprint it was compiled from.
|
|
1804
|
+
*/
|
|
1805
|
+
export declare const isPlan: Guard<Plan>;
|
|
1914
1806
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
*
|
|
1927
|
-
* @example
|
|
1928
|
-
* ```ts
|
|
1929
|
-
* import { manifestToName } from '@orkestrel/scaffold'
|
|
1930
|
-
*
|
|
1931
|
-
* manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router'
|
|
1932
|
-
* manifestToName('{') // undefined
|
|
1933
|
-
* ```
|
|
1934
|
-
*/
|
|
1935
|
-
export declare function manifestToName(manifest: string): string | undefined;
|
|
1807
|
+
/**
|
|
1808
|
+
* Narrow a value to a {@link Question}.
|
|
1809
|
+
*
|
|
1810
|
+
* @example
|
|
1811
|
+
* ```ts
|
|
1812
|
+
* import { isQuestion } from '@orkestrel/scaffold'
|
|
1813
|
+
*
|
|
1814
|
+
* isQuestion({ field: 'src', message: 'Unknown environment', blocking: true }) // true
|
|
1815
|
+
* ```
|
|
1816
|
+
*/
|
|
1817
|
+
export declare const isQuestion: Guard<Question>;
|
|
1936
1818
|
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1819
|
+
/**
|
|
1820
|
+
* Narrow a caught value to a {@link ScaffoldError}.
|
|
1821
|
+
*
|
|
1822
|
+
* @param value - The caught value to narrow.
|
|
1823
|
+
* @returns `true` when `value` is a {@link ScaffoldError}.
|
|
1824
|
+
*
|
|
1825
|
+
* @example
|
|
1826
|
+
* ```ts
|
|
1827
|
+
* import { isScaffoldError } from '@orkestrel/scaffold'
|
|
1828
|
+
*
|
|
1829
|
+
* isScaffoldError(new Error('plain')) // false
|
|
1830
|
+
* isScaffoldError(undefined) // false
|
|
1831
|
+
* ```
|
|
1832
|
+
*/
|
|
1833
|
+
export declare function isScaffoldError(value: unknown): value is ScaffoldError;
|
|
1950
1834
|
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1835
|
+
/**
|
|
1836
|
+
* Narrow a value to a {@link Snapshot}.
|
|
1837
|
+
*
|
|
1838
|
+
* @param value - The candidate target snapshot.
|
|
1839
|
+
* @returns `true` for a bounded plain record whose every key is a path and
|
|
1840
|
+
* whose every value is exact lowercase hexadecimal bytes.
|
|
1841
|
+
*
|
|
1842
|
+
* @remarks
|
|
1843
|
+
* Read through the shared total key lens, so a hostile `ownKeys` trap and a
|
|
1844
|
+
* throwing accessor both answer `false` rather than escaping. There is no
|
|
1845
|
+
* dictionary combinator upstream to compose this from: the key law and the
|
|
1846
|
+
* value law are both this package's own.
|
|
1847
|
+
*
|
|
1848
|
+
* @example
|
|
1849
|
+
* ```ts
|
|
1850
|
+
* import { isSnapshot } from '@orkestrel/scaffold'
|
|
1851
|
+
*
|
|
1852
|
+
* isSnapshot({ 'AGENTS.md': '68690a' }) // true
|
|
1853
|
+
* isSnapshot({ 'AGENTS.md': 'hi' }) // false
|
|
1854
|
+
* ```
|
|
1855
|
+
*/
|
|
1856
|
+
export declare function isSnapshot(value: unknown): value is Snapshot;
|
|
1973
1857
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
* caller follows.
|
|
1986
|
-
*
|
|
1987
|
-
* @example
|
|
1988
|
-
* ```ts
|
|
1989
|
-
* import { matchesOrchestrationPath } from '@orkestrel/scaffold'
|
|
1990
|
-
*
|
|
1991
|
-
* matchesOrchestrationPath('.claude/rules/names.md') // true
|
|
1992
|
-
* matchesOrchestrationPath('.mcp.json') // true
|
|
1993
|
-
* matchesOrchestrationPath('.oxlintrc.json') // false
|
|
1994
|
-
* ```
|
|
1995
|
-
*/
|
|
1996
|
-
export declare function matchesOrchestrationPath(path: string): boolean;
|
|
1858
|
+
/**
|
|
1859
|
+
* Whether an upstream lookup produced an answer.
|
|
1860
|
+
*
|
|
1861
|
+
* @remarks
|
|
1862
|
+
* `found` carries the answer. `missing` is an upstream `404`, which is a
|
|
1863
|
+
* definite answer that the package is not published there. `failed` is a
|
|
1864
|
+
* transport fault, which is no answer at all and may succeed on a later run.
|
|
1865
|
+
* Holding these apart from how a local copy compares is what lets a verdict
|
|
1866
|
+
* omit the value it never received instead of inventing an empty one.
|
|
1867
|
+
*/
|
|
1868
|
+
export declare type Lookup = 'found' | 'missing' | 'failed';
|
|
1997
1869
|
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
1870
|
+
/**
|
|
1871
|
+
* Project a package manifest's text to the `@orkestrel/*` packages it declares.
|
|
1872
|
+
*
|
|
1873
|
+
* @param manifest - The `package.json` text.
|
|
1874
|
+
* @returns One dependency per declared `@orkestrel` package, in section order,
|
|
1875
|
+
* with the first declaration of a repeated name winning.
|
|
1876
|
+
*
|
|
1877
|
+
* @remarks
|
|
1878
|
+
* Runtime, development, and peer sections are read in that order, because a
|
|
1879
|
+
* package the fleet publishes is upstream of this workspace wherever it is
|
|
1880
|
+
* declared. Every other name is skipped rather than refused: a workspace's
|
|
1881
|
+
* unrelated dependencies are not this package's to report on.
|
|
1882
|
+
*
|
|
1883
|
+
* Never throws, and every row it returns satisfies `isDependency` while the
|
|
1884
|
+
* list satisfies `isCollection`, so the result crosses the compiler's own
|
|
1885
|
+
* boundary without a second cleaning.
|
|
1886
|
+
*
|
|
1887
|
+
* @example
|
|
1888
|
+
* ```ts
|
|
1889
|
+
* import { manifestToDependencies } from '@orkestrel/scaffold'
|
|
1890
|
+
*
|
|
1891
|
+
* manifestToDependencies('{"dependencies":{"@orkestrel/emitter":"^0.0.5","vite":"~8.2.0"}}')
|
|
1892
|
+
* // [{ name: '@orkestrel/emitter', range: '^0.0.5' }]
|
|
1893
|
+
* ```
|
|
1894
|
+
*/
|
|
1895
|
+
export declare function manifestToDependencies(manifest: string): readonly Dependency[];
|
|
2020
1896
|
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
* range, and a caret range. An exact pin is satisfied by that version alone. A
|
|
2043
|
-
* tilde range holds the minor and admits a later patch. A caret range holds the
|
|
2044
|
-
* leading nonzero component, which is why `^0.0.5` is an exact pin and `^0.5.3`
|
|
2045
|
-
* admits `0.5.4` but not `0.6.0`.
|
|
2046
|
-
*
|
|
2047
|
-
* Identity is the door a prerelease passes through, and the only one: this
|
|
2048
|
-
* package does not order prerelease precedence, so `1.2.3-beta.1` is satisfied
|
|
2049
|
-
* by that exact string and by nothing else. The version side needs no separate
|
|
2050
|
-
* readability test, because identity already proves it equals a readable
|
|
2051
|
-
* declaration and every other branch reads it through {@link extractVersion}.
|
|
2052
|
-
*
|
|
2053
|
-
* @example
|
|
2054
|
-
* ```ts
|
|
2055
|
-
* import { matchesRange } from '@orkestrel/scaffold'
|
|
2056
|
-
*
|
|
2057
|
-
* matchesRange('^0.0.5', '0.0.5') // true
|
|
2058
|
-
* matchesRange('^0.0.5', '0.0.7') // false
|
|
2059
|
-
* matchesRange('~8.2.0', '8.2.4') // true
|
|
2060
|
-
* matchesRange('^7.58.12', '7.60.0') // true
|
|
2061
|
-
* matchesRange('not-a-range', 'not-a-range') // false
|
|
2062
|
-
* ```
|
|
2063
|
-
*/
|
|
2064
|
-
export declare function matchesRange(range: string, latest: string): boolean;
|
|
1897
|
+
/**
|
|
1898
|
+
* Project a package manifest's text to its own name.
|
|
1899
|
+
*
|
|
1900
|
+
* @param manifest - The `package.json` text.
|
|
1901
|
+
* @returns The declared name, or `undefined` when the text is oversized,
|
|
1902
|
+
* malformed, not an object, or carries no bounded string name.
|
|
1903
|
+
*
|
|
1904
|
+
* @remarks
|
|
1905
|
+
* Never throws: a manifest is a file a target owns, so unreadable is an answer
|
|
1906
|
+
* rather than a fault. The name is bounded by the registry's own package-name
|
|
1907
|
+
* ceiling, because it reaches a path through {@link nameToGuide}.
|
|
1908
|
+
*
|
|
1909
|
+
* @example
|
|
1910
|
+
* ```ts
|
|
1911
|
+
* import { manifestToName } from '@orkestrel/scaffold'
|
|
1912
|
+
*
|
|
1913
|
+
* manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router'
|
|
1914
|
+
* manifestToName('{') // undefined
|
|
1915
|
+
* ```
|
|
1916
|
+
*/
|
|
1917
|
+
export declare function manifestToName(manifest: string): string | undefined;
|
|
2065
1918
|
|
|
2066
|
-
|
|
2067
|
-
|
|
1919
|
+
/**
|
|
1920
|
+
* Test whether {@link inferDrift} could have produced a finding for an ownership.
|
|
1921
|
+
*
|
|
1922
|
+
* @param ownership - What scaffold claims at the planned path.
|
|
1923
|
+
* @param finding - The audit verdict to test.
|
|
1924
|
+
* @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
|
|
1925
|
+
*
|
|
1926
|
+
* @remarks
|
|
1927
|
+
* This predicate keeps the comparison law beside the reachability law it
|
|
1928
|
+
* restates. A mutation uses it so a refusal can distinguish an impossible
|
|
1929
|
+
* verdict from a target that genuinely moved after its audit.
|
|
1930
|
+
*/
|
|
1931
|
+
export declare function matchesDriftReachability(ownership: Ownership, finding: Finding): boolean;
|
|
2068
1932
|
|
|
2069
|
-
|
|
2070
|
-
|
|
1933
|
+
/**
|
|
1934
|
+
* Test whether a declared engines floor is at or above the supported minimum.
|
|
1935
|
+
*
|
|
1936
|
+
* @param engines - The declared `engines.node` range.
|
|
1937
|
+
* @returns `true` when the range is the accepted syntax and its floor is at or
|
|
1938
|
+
* above `MINIMUM_NODE_VERSION`.
|
|
1939
|
+
*
|
|
1940
|
+
* @remarks
|
|
1941
|
+
* The declaration states a floor, so the comparison is against the oldest Node
|
|
1942
|
+
* the generated toolchain supports rather than against a published version. The
|
|
1943
|
+
* `>=` prefix is read here, so no caller repeats the offset.
|
|
1944
|
+
*
|
|
1945
|
+
* @example
|
|
1946
|
+
* ```ts
|
|
1947
|
+
* import { matchesEngines } from '@orkestrel/scaffold'
|
|
1948
|
+
*
|
|
1949
|
+
* matchesEngines('>=22.12.0') // true
|
|
1950
|
+
* matchesEngines('>=20.0.0') // false
|
|
1951
|
+
* matchesEngines('22.12.0') // false
|
|
1952
|
+
* ```
|
|
1953
|
+
*/
|
|
1954
|
+
export declare function matchesEngines(engines: string): boolean;
|
|
2071
1955
|
|
|
2072
|
-
|
|
2073
|
-
|
|
1956
|
+
/**
|
|
1957
|
+
* Test whether a path instructs or wires an agent rather than the toolchain.
|
|
1958
|
+
*
|
|
1959
|
+
* @param path - The target-relative path to test.
|
|
1960
|
+
* @returns `true` when the path is beneath a harness directory or is one of the
|
|
1961
|
+
* exact root filenames that wires an agent bench.
|
|
1962
|
+
*
|
|
1963
|
+
* @remarks
|
|
1964
|
+
* The one home of the orchestration membership rule. A vendored path and a
|
|
1965
|
+
* foreign path found in a target are both classified through here, so a new
|
|
1966
|
+
* harness directory is admitted once in `ORCHESTRATION_PATH_PREFIXES` and every
|
|
1967
|
+
* caller follows.
|
|
1968
|
+
*
|
|
1969
|
+
* @example
|
|
1970
|
+
* ```ts
|
|
1971
|
+
* import { matchesOrchestrationPath } from '@orkestrel/scaffold'
|
|
1972
|
+
*
|
|
1973
|
+
* matchesOrchestrationPath('.claude/rules/names.md') // true
|
|
1974
|
+
* matchesOrchestrationPath('.mcp.json') // true
|
|
1975
|
+
* matchesOrchestrationPath('.oxlintrc.json') // false
|
|
1976
|
+
* ```
|
|
1977
|
+
*/
|
|
1978
|
+
export declare function matchesOrchestrationPath(path: string): boolean;
|
|
2074
1979
|
|
|
2075
|
-
|
|
2076
|
-
|
|
1980
|
+
/**
|
|
1981
|
+
* Test whether one emitted line fits the vendored formatter width.
|
|
1982
|
+
*
|
|
1983
|
+
* @param line - One emitted line, leading tabs included.
|
|
1984
|
+
* @returns `true` when the expanded line fits.
|
|
1985
|
+
*
|
|
1986
|
+
* @remarks
|
|
1987
|
+
* A generator writes source the formatter then reads back, so a line packed
|
|
1988
|
+
* past the vendored width is rewrapped on the next `format` run and the emitted
|
|
1989
|
+
* bytes stop matching the plan the audit compares against. The generator
|
|
1990
|
+
* therefore measures a candidate line and chooses the shape the formatter would
|
|
1991
|
+
* have chosen. Tabs are expanded first because the formatter counts them as
|
|
1992
|
+
* `TAB_WIDTH` columns rather than as one character.
|
|
1993
|
+
*
|
|
1994
|
+
* @example
|
|
1995
|
+
* ```ts
|
|
1996
|
+
* import { matchesPrintWidth } from '@orkestrel/scaffold'
|
|
1997
|
+
*
|
|
1998
|
+
* matchesPrintWidth('\t\tprojects: [core],') // true
|
|
1999
|
+
* ```
|
|
2000
|
+
*/
|
|
2001
|
+
export declare function matchesPrintWidth(line: string): boolean;
|
|
2077
2002
|
|
|
2078
|
-
|
|
2079
|
-
|
|
2003
|
+
/**
|
|
2004
|
+
* Test whether a declared range already admits a published version.
|
|
2005
|
+
*
|
|
2006
|
+
* @param range - The declared dependency range.
|
|
2007
|
+
* @param latest - The version the registry reported as latest.
|
|
2008
|
+
* @returns `true` when the range admits that version.
|
|
2009
|
+
*
|
|
2010
|
+
* @remarks
|
|
2011
|
+
* The one place this comparison is made. A `Release` records the declared range
|
|
2012
|
+
* and the reported version and stores no verdict beside them, because a stored
|
|
2013
|
+
* verdict could only disagree with the fields it sits next to.
|
|
2014
|
+
*
|
|
2015
|
+
* Readability is decided first, and it is `EXTRA_RANGE_PATTERN`: an optional
|
|
2016
|
+
* caret or tilde over `major.minor.patch` and an optional prerelease
|
|
2017
|
+
* suffix. That pattern already covers every `ORKESTREL_RANGE_PATTERN` range and
|
|
2018
|
+
* every `VERSION_PATTERN` version, so the subset is stated once rather than
|
|
2019
|
+
* assembled here. Text outside it is never admitted, including text handed in on
|
|
2020
|
+
* both sides, so an unreadable declaration surfaces as work instead of matching
|
|
2021
|
+
* itself.
|
|
2022
|
+
*
|
|
2023
|
+
* The accepted subset is the one that pattern admits: an exact pin, a tilde
|
|
2024
|
+
* range, and a caret range. An exact pin is satisfied by that version alone. A
|
|
2025
|
+
* tilde range holds the minor and admits a later patch. A caret range holds the
|
|
2026
|
+
* leading nonzero component, which is why `^0.0.5` is an exact pin and `^0.5.3`
|
|
2027
|
+
* admits `0.5.4` but not `0.6.0`.
|
|
2028
|
+
*
|
|
2029
|
+
* Identity is the door a prerelease passes through, and the only one: this
|
|
2030
|
+
* package does not order prerelease precedence, so `1.2.3-beta.1` is satisfied
|
|
2031
|
+
* by that exact string and by nothing else. The version side needs no separate
|
|
2032
|
+
* readability test, because identity already proves it equals a readable
|
|
2033
|
+
* declaration and every other branch reads it through {@link extractVersion}.
|
|
2034
|
+
*
|
|
2035
|
+
* @example
|
|
2036
|
+
* ```ts
|
|
2037
|
+
* import { matchesRange } from '@orkestrel/scaffold'
|
|
2038
|
+
*
|
|
2039
|
+
* matchesRange('^0.0.5', '0.0.5') // true
|
|
2040
|
+
* matchesRange('^0.0.5', '0.0.7') // false
|
|
2041
|
+
* matchesRange('~8.2.0', '8.2.4') // true
|
|
2042
|
+
* matchesRange('^7.58.12', '7.60.0') // true
|
|
2043
|
+
* matchesRange('not-a-range', 'not-a-range') // false
|
|
2044
|
+
* ```
|
|
2045
|
+
*/
|
|
2046
|
+
export declare function matchesRange(range: string, latest: string): boolean;
|
|
2080
2047
|
|
|
2081
|
-
|
|
2082
|
-
|
|
2048
|
+
/** Maximum bytes accepted for one artifact. */
|
|
2049
|
+
export declare const MAX_ARTIFACT_BYTES = 5242880;
|
|
2083
2050
|
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
*
|
|
2087
|
-
* @remarks
|
|
2088
|
-
* The registry caps a whole package name at 214 characters and the generated
|
|
2089
|
-
* scope `@orkestrel/` spends 11 of them.
|
|
2090
|
-
*/
|
|
2091
|
-
export declare const MAX_NAME_LENGTH = 203;
|
|
2051
|
+
/** Maximum length of the hexadecimal string carrying one artifact's bytes. */
|
|
2052
|
+
export declare const MAX_ARTIFACT_HEX_LENGTH: number;
|
|
2092
2053
|
|
|
2093
|
-
|
|
2094
|
-
|
|
2054
|
+
/** Maximum findings one audit can produce from a bounded plan and snapshot. */
|
|
2055
|
+
export declare const MAX_AUDIT_FINDINGS: number;
|
|
2095
2056
|
|
|
2096
|
-
|
|
2097
|
-
|
|
2057
|
+
/** Maximum items accepted in one public collection. */
|
|
2058
|
+
export declare const MAX_COLLECTION_ITEMS = 1000;
|
|
2098
2059
|
|
|
2099
|
-
|
|
2100
|
-
|
|
2060
|
+
/** Maximum dependency package name length, scope included, as the registry caps it. */
|
|
2061
|
+
export declare const MAX_DEPENDENCY_NAME_LENGTH = 214;
|
|
2101
2062
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2063
|
+
/** Maximum bytes accepted for one package or vendored-host manifest. */
|
|
2064
|
+
export declare const MAX_MANIFEST_BYTES = 1048576;
|
|
2104
2065
|
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
* {@link Finding.observed} is. Whether the mirror is behind is not recorded,
|
|
2114
|
-
* because it is `content` against `observed` and a stored answer could only
|
|
2115
|
-
* disagree with them. These bytes belong to the catalog verb, which is why a
|
|
2116
|
-
* guide mirror is presence-owned: repair restores one that is absent and never
|
|
2117
|
-
* replaces one that is present.
|
|
2118
|
-
*/
|
|
2119
|
-
export declare type Mirror = {
|
|
2120
|
-
readonly name: string;
|
|
2121
|
-
readonly path: string;
|
|
2122
|
-
readonly lookup: 'found';
|
|
2123
|
-
readonly content: string;
|
|
2124
|
-
readonly observed?: string;
|
|
2125
|
-
readonly note?: never;
|
|
2126
|
-
} | {
|
|
2127
|
-
readonly name: string;
|
|
2128
|
-
readonly path: string;
|
|
2129
|
-
readonly lookup: 'missing' | 'failed';
|
|
2130
|
-
readonly note: string;
|
|
2131
|
-
readonly observed?: string;
|
|
2132
|
-
readonly content?: never;
|
|
2133
|
-
};
|
|
2134
|
-
|
|
2135
|
-
/** The bare workspace name syntax: lowercase alphanumeric with hyphens, letter first. */
|
|
2136
|
-
export declare const NAME_PATTERN: RegExp;
|
|
2137
|
-
|
|
2138
|
-
/**
|
|
2139
|
-
* Derive the guide mirror path a package name answers for.
|
|
2140
|
-
*
|
|
2141
|
-
* @param name - A bare or `@orkestrel`-scoped package name.
|
|
2142
|
-
* @returns The mirror path, `guides/<bare name>.md`.
|
|
2143
|
-
*
|
|
2144
|
-
* @remarks
|
|
2145
|
-
* The single shape of a mirror path, read by the fetch that fills one and by
|
|
2146
|
-
* the plan that leaves the target's own guide out of its host set. Only the
|
|
2147
|
-
* segment after the final `/` is used, so the scope never reaches the path.
|
|
2148
|
-
* Whether the derived path is safe to write is `isPath`'s answer and the gate's,
|
|
2149
|
-
* which is why `DEPENDENCY_NAME_PATTERN` closes the name to a bare scoped one
|
|
2150
|
-
* before it ever arrives here.
|
|
2151
|
-
*
|
|
2152
|
-
* @example
|
|
2153
|
-
* ```ts
|
|
2154
|
-
* import { nameToGuide } from '@orkestrel/scaffold'
|
|
2155
|
-
*
|
|
2156
|
-
* nameToGuide('@orkestrel/router') // 'guides/router.md'
|
|
2157
|
-
* nameToGuide('scaffold') // 'guides/scaffold.md'
|
|
2158
|
-
* ```
|
|
2159
|
-
*/
|
|
2160
|
-
export declare function nameToGuide(name: string): string;
|
|
2066
|
+
/**
|
|
2067
|
+
* Maximum bare workspace name length.
|
|
2068
|
+
*
|
|
2069
|
+
* @remarks
|
|
2070
|
+
* The registry caps a whole package name at 214 characters and the generated
|
|
2071
|
+
* scope `@orkestrel/` spends 11 of them.
|
|
2072
|
+
*/
|
|
2073
|
+
export declare const MAX_NAME_LENGTH = 203;
|
|
2161
2074
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
*
|
|
2165
|
-
* @param name - The target workspace's own bare package name.
|
|
2166
|
-
* @returns One artifact per vendored path, in `HOST_PATHS` order.
|
|
2167
|
-
*
|
|
2168
|
-
* @remarks
|
|
2169
|
-
* Every artifact is claimed by presence, which is the strongest claim a pure
|
|
2170
|
-
* compile can make: core cannot read the vendored data root, so it cannot carry
|
|
2171
|
-
* the bytes a content claim would have to be checked against. Reading that root
|
|
2172
|
-
* is what promotes the ones scaffold owns the bytes of.
|
|
2173
|
-
*
|
|
2174
|
-
* `source` is left absent because it falls back to `path`, and every vendored
|
|
2175
|
-
* path is stored under the name it is written to. The group comes from
|
|
2176
|
-
* {@link inferGroup}, so a vendored path and a foreign path found in a target
|
|
2177
|
-
* are classified by one rule and a plan never disagrees with the audit beside
|
|
2178
|
-
* it.
|
|
2179
|
-
*
|
|
2180
|
-
* @example
|
|
2181
|
-
* ```ts
|
|
2182
|
-
* import { nameToHostArtifacts } from '@orkestrel/scaffold'
|
|
2183
|
-
*
|
|
2184
|
-
* nameToHostArtifacts('router').some((artifact) => artifact.path === 'AGENTS.md') // true
|
|
2185
|
-
* nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
|
|
2186
|
-
* ```
|
|
2187
|
-
*/
|
|
2188
|
-
export declare function nameToHostArtifacts(name: string): readonly Artifact[];
|
|
2075
|
+
/** Maximum length of one path, matching the longest a supported filesystem accepts. */
|
|
2076
|
+
export declare const MAX_PATH_LENGTH = 32767;
|
|
2189
2077
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
*
|
|
2193
|
-
* @param name - The workspace's own bare package name.
|
|
2194
|
-
* @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
|
|
2195
|
-
* fills its `{{replacement}}` span with, indented for that span.
|
|
2196
|
-
*
|
|
2197
|
-
* @remarks
|
|
2198
|
-
* `vite-plugin-dts` rolls a face into one declaration and keeps each source
|
|
2199
|
-
* module's own relative depth, so a nested module emits a path that escapes
|
|
2200
|
-
* `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
|
|
2201
|
-
* relative core path to the package's published root export, so the branch is
|
|
2202
|
-
* derived once here. The extension alternation is what the two permitted import
|
|
2203
|
-
* spellings produce: an `@src/core` alias resolves to the core source module and
|
|
2204
|
-
* prints `.ts`, while a relative import prints the `.js` specifier it was
|
|
2205
|
-
* written with. The formatter keeps the call on one line only while the line it
|
|
2206
|
-
* prints measures inside the vendored width, and the workspace name is what
|
|
2207
|
-
* varies, so the shape is chosen by measuring the candidate: a tab prints as the
|
|
2208
|
-
* vendored two columns, and the gate admits a name long enough to push the
|
|
2209
|
-
* joined call past 100.
|
|
2210
|
-
*
|
|
2211
|
-
* @example
|
|
2212
|
-
* ```ts
|
|
2213
|
-
* import { nameToRewrite } from '@orkestrel/scaffold'
|
|
2214
|
-
*
|
|
2215
|
-
* nameToRewrite('router').includes("'@orkestrel/router'") // true
|
|
2216
|
-
* ```
|
|
2217
|
-
*/
|
|
2218
|
-
export declare function nameToRewrite(name: string): string;
|
|
2078
|
+
/** Maximum length of one declared package range. */
|
|
2079
|
+
export declare const MAX_RANGE_LENGTH = 2048;
|
|
2219
2080
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
*
|
|
2223
|
-
* @remarks
|
|
2224
|
-
* `.mcp.json` registers MCP servers for the harness. It sits among the root
|
|
2225
|
-
* dotfiles but governs agents, so it groups with the harness bridges.
|
|
2226
|
-
*/
|
|
2227
|
-
export declare const ORCHESTRATION_PATH_NAMES: readonly string[];
|
|
2081
|
+
/** Maximum bytes retained across one whole plan or audit. */
|
|
2082
|
+
export declare const MAX_TOTAL_ARTIFACT_BYTES = 104857600;
|
|
2228
2083
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
*
|
|
2232
|
-
* @remarks
|
|
2233
|
-
* A path is grouped by what it governs rather than by where it sits: anything
|
|
2234
|
-
* beneath one of these prefixes is `orchestration`, and everything else that is
|
|
2235
|
-
* not source, tests, guides, docs, or a manifest is `configs`. A vendored path
|
|
2236
|
-
* and a foreign path found in a target are classified against the same list, so
|
|
2237
|
-
* a new harness directory is admitted once.
|
|
2238
|
-
*/
|
|
2239
|
-
export declare const ORCHESTRATION_PATH_PREFIXES: readonly string[];
|
|
2084
|
+
/** The oldest Node version the generated toolchain supports. */
|
|
2085
|
+
export declare const MINIMUM_NODE_VERSION = "22.12.0";
|
|
2240
2086
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2087
|
+
/**
|
|
2088
|
+
* One dependency guide fetched from upstream, beside the local mirror it answers for.
|
|
2089
|
+
*
|
|
2090
|
+
* @remarks
|
|
2091
|
+
* A found lookup carries the fetched bytes; one that produced no answer carries
|
|
2092
|
+
* the cause and no bytes. Either way `observed` is the local mirror's exact
|
|
2093
|
+
* bytes as they stood when the fetch was made, and is absent when the mirror
|
|
2094
|
+
* was not there; it is the precondition the write is held to, exactly as
|
|
2095
|
+
* {@link Finding.observed} is. Whether the mirror is behind is not recorded,
|
|
2096
|
+
* because it is `content` against `observed` and a stored answer could only
|
|
2097
|
+
* disagree with them. These bytes belong to the catalog verb, which is why a
|
|
2098
|
+
* guide mirror is presence-owned: repair restores one that is absent and never
|
|
2099
|
+
* replaces one that is present.
|
|
2100
|
+
*/
|
|
2101
|
+
export declare type Mirror = {
|
|
2102
|
+
readonly name: string;
|
|
2103
|
+
readonly path: string;
|
|
2104
|
+
readonly lookup: 'found';
|
|
2105
|
+
readonly content: string;
|
|
2106
|
+
readonly observed?: string;
|
|
2107
|
+
readonly note?: never;
|
|
2108
|
+
} | {
|
|
2109
|
+
readonly name: string;
|
|
2110
|
+
readonly path: string;
|
|
2111
|
+
readonly lookup: 'missing' | 'failed';
|
|
2112
|
+
readonly note: string;
|
|
2113
|
+
readonly observed?: string;
|
|
2114
|
+
readonly content?: never;
|
|
2115
|
+
};
|
|
2116
|
+
|
|
2117
|
+
/** The bare workspace name syntax: lowercase alphanumeric with hyphens, letter first. */
|
|
2118
|
+
export declare const NAME_PATTERN: RegExp;
|
|
2251
2119
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2120
|
+
/**
|
|
2121
|
+
* Derive the guide mirror path a package name answers for.
|
|
2122
|
+
*
|
|
2123
|
+
* @param name - A bare or `@orkestrel`-scoped package name.
|
|
2124
|
+
* @returns The mirror path, `guides/<bare name>.md`.
|
|
2125
|
+
*
|
|
2126
|
+
* @remarks
|
|
2127
|
+
* The single shape of a mirror path, read by the fetch that fills one and by
|
|
2128
|
+
* the plan that leaves the target's own guide out of its host set. Only the
|
|
2129
|
+
* segment after the final `/` is used, so the scope never reaches the path.
|
|
2130
|
+
* Whether the derived path is safe to write is `isPath`'s answer and the gate's,
|
|
2131
|
+
* which is why `DEPENDENCY_NAME_PATTERN` closes the name to a bare scoped one
|
|
2132
|
+
* before it ever arrives here.
|
|
2133
|
+
*
|
|
2134
|
+
* @example
|
|
2135
|
+
* ```ts
|
|
2136
|
+
* import { nameToGuide } from '@orkestrel/scaffold'
|
|
2137
|
+
*
|
|
2138
|
+
* nameToGuide('@orkestrel/router') // 'guides/router.md'
|
|
2139
|
+
* nameToGuide('scaffold') // 'guides/scaffold.md'
|
|
2140
|
+
* ```
|
|
2141
|
+
*/
|
|
2142
|
+
export declare function nameToGuide(name: string): string;
|
|
2262
2143
|
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2144
|
+
/**
|
|
2145
|
+
* Compile the vendored host artifacts a named workspace plans.
|
|
2146
|
+
*
|
|
2147
|
+
* @param name - The target workspace's own bare package name.
|
|
2148
|
+
* @returns One artifact per vendored path, in `HOST_PATHS` order.
|
|
2149
|
+
*
|
|
2150
|
+
* @remarks
|
|
2151
|
+
* Every artifact is claimed by presence, which is the strongest claim a pure
|
|
2152
|
+
* compile can make: core cannot read the vendored data root, so it cannot carry
|
|
2153
|
+
* the bytes a content claim would have to be checked against. Reading that root
|
|
2154
|
+
* is what promotes the ones scaffold owns the bytes of.
|
|
2155
|
+
*
|
|
2156
|
+
* `source` is left absent because it falls back to `path`, and every vendored
|
|
2157
|
+
* path is stored under the name it is written to. The group comes from
|
|
2158
|
+
* {@link inferGroup}, so a vendored path and a foreign path found in a target
|
|
2159
|
+
* are classified by one rule and a plan never disagrees with the audit beside
|
|
2160
|
+
* it.
|
|
2161
|
+
*
|
|
2162
|
+
* @example
|
|
2163
|
+
* ```ts
|
|
2164
|
+
* import { nameToHostArtifacts } from '@orkestrel/scaffold'
|
|
2165
|
+
*
|
|
2166
|
+
* nameToHostArtifacts('router').some((artifact) => artifact.path === 'AGENTS.md') // true
|
|
2167
|
+
* nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
|
|
2168
|
+
* ```
|
|
2169
|
+
*/
|
|
2170
|
+
export declare function nameToHostArtifacts(name: string): readonly Artifact[];
|
|
2277
2171
|
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2172
|
+
/**
|
|
2173
|
+
* Derive the declaration rewrite a published face's `beforeWriteFile` applies.
|
|
2174
|
+
*
|
|
2175
|
+
* @param name - The workspace's own bare package name.
|
|
2176
|
+
* @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
|
|
2177
|
+
* fills its `{{replacement}}` span with, indented for that span.
|
|
2178
|
+
*
|
|
2179
|
+
* @remarks
|
|
2180
|
+
* `vite-plugin-dts` rolls a face into one declaration and keeps each source
|
|
2181
|
+
* module's own relative depth, so a nested module emits a path that escapes
|
|
2182
|
+
* `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
|
|
2183
|
+
* relative core path to the package's published root export, so the branch is
|
|
2184
|
+
* derived once here. The extension alternation is what the permitted import
|
|
2185
|
+
* spellings produce: an `@src/core` alias resolves to the core source module and
|
|
2186
|
+
* prints `.ts`, while a relative import prints the `.js` specifier it was
|
|
2187
|
+
* written with. The formatter keeps the call on one line only while the line it
|
|
2188
|
+
* prints measures inside the vendored width, and the workspace name is what
|
|
2189
|
+
* varies, so the shape is chosen by measuring the candidate: a tab prints as the
|
|
2190
|
+
* vendored two columns, and the gate admits a name long enough to push the
|
|
2191
|
+
* joined call past 100.
|
|
2192
|
+
*
|
|
2193
|
+
* @example
|
|
2194
|
+
* ```ts
|
|
2195
|
+
* import { nameToRewrite } from '@orkestrel/scaffold'
|
|
2196
|
+
*
|
|
2197
|
+
* nameToRewrite('router').includes("'@orkestrel/router'") // true
|
|
2198
|
+
* ```
|
|
2199
|
+
*/
|
|
2200
|
+
export declare function nameToRewrite(name: string): string;
|
|
2304
2201
|
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
* never compares it and always reports it aligned, and a write creates it only
|
|
2314
|
-
* while it is absent.
|
|
2315
|
-
*/
|
|
2316
|
-
export declare type Ownership = 'content' | 'presence' | 'birth';
|
|
2202
|
+
/**
|
|
2203
|
+
* The exact root filenames that wire an agent bench rather than the toolchain, frozen.
|
|
2204
|
+
*
|
|
2205
|
+
* @remarks
|
|
2206
|
+
* `.mcp.json` registers MCP servers for the harness. It sits among the root
|
|
2207
|
+
* dotfiles but governs agents, so it groups with the harness bridges.
|
|
2208
|
+
*/
|
|
2209
|
+
export declare const ORCHESTRATION_PATH_NAMES: readonly string[];
|
|
2317
2210
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
*
|
|
2330
|
-
* @example
|
|
2331
|
-
* ```ts
|
|
2332
|
-
* import { parseBlueprint } from '@orkestrel/scaffold'
|
|
2333
|
-
*
|
|
2334
|
-
* parseBlueprint({ name: 'router' }) // undefined
|
|
2335
|
-
* ```
|
|
2336
|
-
*/
|
|
2337
|
-
export declare function parseBlueprint(value: unknown): Blueprint | undefined;
|
|
2211
|
+
/**
|
|
2212
|
+
* The path prefixes whose contents instruct or wire an agent, frozen.
|
|
2213
|
+
*
|
|
2214
|
+
* @remarks
|
|
2215
|
+
* A path is grouped by what it governs rather than by where it sits: anything
|
|
2216
|
+
* beneath one of these prefixes is `orchestration`, and everything else that is
|
|
2217
|
+
* not source, tests, guides, docs, or a manifest is `configs`. A vendored path
|
|
2218
|
+
* and a foreign path found in a target are classified against the same list, so
|
|
2219
|
+
* a new harness directory is admitted once.
|
|
2220
|
+
*/
|
|
2221
|
+
export declare const ORCHESTRATION_PATH_PREFIXES: readonly string[];
|
|
2338
2222
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
*
|
|
2350
|
-
* @example
|
|
2351
|
-
* ```ts
|
|
2352
|
-
* import { parseCompilerOptions } from '@orkestrel/scaffold'
|
|
2353
|
-
*
|
|
2354
|
-
* parseCompilerOptions({ on: { compile: () => {} } }) // the same record
|
|
2355
|
-
* parseCompilerOptions({ on: { compiled: () => {} } }) // undefined
|
|
2356
|
-
* ```
|
|
2357
|
-
*/
|
|
2358
|
-
export declare function parseCompilerOptions(value: unknown): CompilerOptions | undefined;
|
|
2223
|
+
/**
|
|
2224
|
+
* How an artifact's content is produced.
|
|
2225
|
+
*
|
|
2226
|
+
* @remarks
|
|
2227
|
+
* `host` is byte-copied from this package's vendored data root. `template` is
|
|
2228
|
+
* filled from a frozen template definition. `computed` is derived by this
|
|
2229
|
+
* package's own combination logic. Origin says nothing about what scaffold
|
|
2230
|
+
* claims at the path; {@link Ownership} says that.
|
|
2231
|
+
*/
|
|
2232
|
+
export declare type Origin = 'host' | 'template' | 'computed';
|
|
2359
2233
|
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
*
|
|
2371
|
-
* @example
|
|
2372
|
-
* ```ts
|
|
2373
|
-
* import { parseGroups } from '@orkestrel/scaffold'
|
|
2374
|
-
*
|
|
2375
|
-
* parseGroups(['manifest', 'configs']) // ['manifest', 'configs']
|
|
2376
|
-
* parseGroups(['readme']) // undefined
|
|
2377
|
-
* ```
|
|
2378
|
-
*/
|
|
2379
|
-
export declare function parseGroups(value: unknown): readonly Group[] | undefined;
|
|
2234
|
+
/**
|
|
2235
|
+
* The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency.
|
|
2236
|
+
*
|
|
2237
|
+
* @remarks
|
|
2238
|
+
* Pre-1.0 means any `0.x`, not `0.0.x`. The narrower form would refuse the first
|
|
2239
|
+
* fleet package to reach `0.1.0`, and `catalog` pins to whatever the registry
|
|
2240
|
+
* publishes, so a single minor release would block every later run against a
|
|
2241
|
+
* workspace that had already been pinned to it.
|
|
2242
|
+
*/
|
|
2243
|
+
export declare const ORKESTREL_RANGE_PATTERN: RegExp;
|
|
2380
2244
|
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
* parseSnapshot({ 'AGENTS.md': null }) // undefined
|
|
2396
|
-
* ```
|
|
2397
|
-
*/
|
|
2398
|
-
export declare function parseSnapshot(value: unknown): Snapshot | undefined;
|
|
2245
|
+
/**
|
|
2246
|
+
* One artifact override.
|
|
2247
|
+
*
|
|
2248
|
+
* @remarks
|
|
2249
|
+
* `content` replaces the rendered artifact at `path` and never partially
|
|
2250
|
+
* merges it. Legality is measured against every artifact the blueprint drafts,
|
|
2251
|
+
* before a compile narrows the returned groups. An override that matches none
|
|
2252
|
+
* of those artifacts, that targets a host-origin artifact, or that targets the
|
|
2253
|
+
* manifest is a blocking question rather than a silent no-op.
|
|
2254
|
+
*/
|
|
2255
|
+
export declare interface Override {
|
|
2256
|
+
readonly path: string;
|
|
2257
|
+
readonly content: string;
|
|
2258
|
+
}
|
|
2399
2259
|
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2260
|
+
/**
|
|
2261
|
+
* Measure a blueprint's overrides against the artifacts drafted for it.
|
|
2262
|
+
*
|
|
2263
|
+
* @param overrides - The blueprint's overrides.
|
|
2264
|
+
* @param artifacts - The drafted artifacts, before overrides are applied.
|
|
2265
|
+
* @returns One blocking question per override the draft cannot accept.
|
|
2266
|
+
*
|
|
2267
|
+
* @remarks
|
|
2268
|
+
* An override that matches no planned artifact is a caller expecting a file that
|
|
2269
|
+
* does not exist, and applying nothing would leave that expectation
|
|
2270
|
+
* unanswered. An override on a host-origin artifact asks this package to
|
|
2271
|
+
* rewrite a file it byte-copies from the vendored data root, which it never
|
|
2272
|
+
* does. An override on the manifest asks it to rewrite the one artifact the
|
|
2273
|
+
* blueprint's own fields decide, so the fields would no longer describe the
|
|
2274
|
+
* workspace they generated. Each is refused rather than dropped.
|
|
2275
|
+
*
|
|
2276
|
+
* @example
|
|
2277
|
+
* ```ts
|
|
2278
|
+
* import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
|
|
2279
|
+
*
|
|
2280
|
+
* const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
|
|
2281
|
+
*
|
|
2282
|
+
* overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
|
|
2283
|
+
* ```
|
|
2284
|
+
*/
|
|
2285
|
+
export declare function overridesToQuestions(overrides: readonly Override[], artifacts: readonly Artifact[]): readonly Question[];
|
|
2423
2286
|
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
/** The tally of one plan by artifact origin. */
|
|
2439
|
-
export declare interface PlanSummary {
|
|
2440
|
-
readonly name: string;
|
|
2441
|
-
readonly src: readonly Environment[];
|
|
2442
|
-
readonly app: readonly Environment[];
|
|
2443
|
-
readonly groups: readonly Group[];
|
|
2444
|
-
readonly host: number;
|
|
2445
|
-
readonly template: number;
|
|
2446
|
-
readonly computed: number;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
/**
|
|
2450
|
-
* Compare a plan against a target's current content.
|
|
2451
|
-
*
|
|
2452
|
-
* @param plan - The compiled plan.
|
|
2453
|
-
* @param current - The target's exact bytes, keyed by artifact-relative path.
|
|
2454
|
-
* @returns One finding per planned artifact in plan order, then one `foreign`
|
|
2455
|
-
* finding per unplanned path in snapshot order.
|
|
2456
|
-
*
|
|
2457
|
-
* @remarks
|
|
2458
|
-
* The sweep is bounded by the plan's own selection: a path the plan does not own
|
|
2459
|
-
* is reported as foreign only when its group is one the plan covers, so a
|
|
2460
|
-
* compile narrowed to a few groups never reports the rest of the workspace as
|
|
2461
|
-
* unowned. Within a covered group the report is deliberately wide, because
|
|
2462
|
-
* `foreign` is the set the destructive verb draws from and the narrowing that
|
|
2463
|
-
* set needs — the paths no verb may remove, and what git tracks — belongs to
|
|
2464
|
-
* that verb rather than to the comparison.
|
|
2465
|
-
*
|
|
2466
|
-
* @example
|
|
2467
|
-
* ```ts
|
|
2468
|
-
* import { createBlueprint, createCompiler, planToFindings } from '@orkestrel/scaffold'
|
|
2469
|
-
*
|
|
2470
|
-
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2471
|
-
*
|
|
2472
|
-
* plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2473
|
-
* ```
|
|
2474
|
-
*/
|
|
2475
|
-
export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
|
|
2287
|
+
/**
|
|
2288
|
+
* What scaffold claims at an artifact's path.
|
|
2289
|
+
*
|
|
2290
|
+
* @remarks
|
|
2291
|
+
* `content` claims the bytes: audit compares them, and a write restores a
|
|
2292
|
+
* missing file and replaces a stale one. `presence` claims only that the file
|
|
2293
|
+
* exists: audit compares existence, and a write restores an absent file and
|
|
2294
|
+
* never touches present bytes. `birth` claims only the file's creation: audit
|
|
2295
|
+
* never compares it and always reports it aligned, and a write creates it only
|
|
2296
|
+
* while it is absent.
|
|
2297
|
+
*/
|
|
2298
|
+
export declare type Ownership = 'content' | 'presence' | 'birth';
|
|
2476
2299
|
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
* ```ts
|
|
2498
|
-
* import { createBlueprint, createCompiler, planToHash } from '@orkestrel/scaffold'
|
|
2499
|
-
*
|
|
2500
|
-
* const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2501
|
-
*
|
|
2502
|
-
* plan === undefined ? undefined : planToHash(plan)?.length // 16
|
|
2503
|
-
* ```
|
|
2504
|
-
*/
|
|
2505
|
-
export declare function planToHash(plan: Plan): string | undefined;
|
|
2300
|
+
/**
|
|
2301
|
+
* Coerce an untrusted value to a {@link Blueprint}.
|
|
2302
|
+
*
|
|
2303
|
+
* @param value - The value to parse.
|
|
2304
|
+
* @returns The blueprint, or `undefined` when the value is not one.
|
|
2305
|
+
*
|
|
2306
|
+
* @remarks
|
|
2307
|
+
* Derived from {@link isBlueprint}, which is what makes the pair sound in both
|
|
2308
|
+
* directions: a guard-valid value is returned unchanged, and every value this
|
|
2309
|
+
* returns satisfies that guard. The reference is returned rather than copied,
|
|
2310
|
+
* so a caller that means to own the value clones it.
|
|
2311
|
+
*
|
|
2312
|
+
* @example
|
|
2313
|
+
* ```ts
|
|
2314
|
+
* import { parseBlueprint } from '@orkestrel/scaffold'
|
|
2315
|
+
*
|
|
2316
|
+
* parseBlueprint({ name: 'router' }) // undefined
|
|
2317
|
+
* ```
|
|
2318
|
+
*/
|
|
2319
|
+
export declare function parseBlueprint(value: unknown): Blueprint | undefined;
|
|
2506
2320
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
export declare function planToSummary(plan: Plan): PlanSummary;
|
|
2321
|
+
/**
|
|
2322
|
+
* Coerce an untrusted value to {@link CompilerOptions}.
|
|
2323
|
+
*
|
|
2324
|
+
* @param value - The value to parse.
|
|
2325
|
+
* @returns The options, or `undefined` when the value is not an option bag.
|
|
2326
|
+
*
|
|
2327
|
+
* @remarks
|
|
2328
|
+
* Derived from {@link isCompilerOptions}. Absence is not an option bag, so
|
|
2329
|
+
* `undefined` in returns `undefined` out and a constructor reads that as the
|
|
2330
|
+
* defaults rather than as a refusal.
|
|
2331
|
+
*
|
|
2332
|
+
* @example
|
|
2333
|
+
* ```ts
|
|
2334
|
+
* import { parseCompilerOptions } from '@orkestrel/scaffold'
|
|
2335
|
+
*
|
|
2336
|
+
* parseCompilerOptions({ on: { compile: () => {} } }) // the same record
|
|
2337
|
+
* parseCompilerOptions({ on: { compiled: () => {} } }) // undefined
|
|
2338
|
+
* ```
|
|
2339
|
+
*/
|
|
2340
|
+
export declare function parseCompilerOptions(value: unknown): CompilerOptions | undefined;
|
|
2528
2341
|
|
|
2529
|
-
|
|
2530
|
-
|
|
2342
|
+
/**
|
|
2343
|
+
* Coerce an untrusted value to a group selection.
|
|
2344
|
+
*
|
|
2345
|
+
* @param value - The value to parse.
|
|
2346
|
+
* @returns The selection, or `undefined` when the value is not one.
|
|
2347
|
+
*
|
|
2348
|
+
* @remarks
|
|
2349
|
+
* Derived from {@link isGroups}. Order and repetition are preserved: which
|
|
2350
|
+
* groups a plan finally covers, and in which order, is the compiler's to decide
|
|
2351
|
+
* from `GROUPS`, not this boundary's.
|
|
2352
|
+
*
|
|
2353
|
+
* @example
|
|
2354
|
+
* ```ts
|
|
2355
|
+
* import { parseGroups } from '@orkestrel/scaffold'
|
|
2356
|
+
*
|
|
2357
|
+
* parseGroups(['manifest', 'configs']) // ['manifest', 'configs']
|
|
2358
|
+
* parseGroups(['readme']) // undefined
|
|
2359
|
+
* ```
|
|
2360
|
+
*/
|
|
2361
|
+
export declare function parseGroups(value: unknown): readonly Group[] | undefined;
|
|
2531
2362
|
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
* @remarks
|
|
2551
|
-
* A found lookup carries the version upstream reported; one that produced no
|
|
2552
|
-
* answer carries the cause and no version. Whether the declared range already
|
|
2553
|
-
* admits that version is not recorded, because it is a deterministic function
|
|
2554
|
-
* of `range` and `latest`: a stored answer could only disagree with the two
|
|
2555
|
-
* fields beside it. One centralized helper decides it, and every caller reads
|
|
2556
|
-
* the same decision.
|
|
2557
|
-
*/
|
|
2558
|
-
export declare type Release = {
|
|
2559
|
-
readonly name: string;
|
|
2560
|
-
readonly range: string;
|
|
2561
|
-
readonly lookup: 'found';
|
|
2562
|
-
readonly latest: string;
|
|
2563
|
-
readonly note?: never;
|
|
2564
|
-
} | {
|
|
2565
|
-
readonly name: string;
|
|
2566
|
-
readonly range: string;
|
|
2567
|
-
readonly lookup: 'missing' | 'failed';
|
|
2568
|
-
readonly note: string;
|
|
2569
|
-
readonly latest?: never;
|
|
2570
|
-
};
|
|
2571
|
-
|
|
2572
|
-
/**
|
|
2573
|
-
* The one error this package throws, carrying the coded reason it was raised.
|
|
2574
|
-
*
|
|
2575
|
-
* @remarks
|
|
2576
|
-
* Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
|
|
2577
|
-
* any call made after teardown, `TARGET` for a destination that is not what the
|
|
2578
|
-
* caller's observation said it was, `WRITE` for a mutation that could not be
|
|
2579
|
-
* completed, `FETCH` for an upstream read that produced no answer the caller can
|
|
2580
|
-
* be given, and `BLOCKED` for a refused blueprint.
|
|
2581
|
-
*
|
|
2582
|
-
* `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
|
|
2583
|
-
* — this blueprint will not be built — and the questions say which. The compiler
|
|
2584
|
-
* answers its refusal rather than throwing it: the gate fails closed, returns the
|
|
2585
|
-
* questions that closed it, and records `BLOCKED` on its stage, so a caller reads
|
|
2586
|
-
* that refusal from the value it asked for. A verb that creates a workspace
|
|
2587
|
-
* throws it, because it chose the shape and has nothing to hand back. A blocking
|
|
2588
|
-
* question closed the gate; a non-blocking one is a shape this package can
|
|
2589
|
-
* describe and declines to create.
|
|
2590
|
-
*
|
|
2591
|
-
* `context` carries whatever the raising site can say about the failure. It is
|
|
2592
|
-
* `unknown` because nothing narrows it usefully at the catch site; read it for
|
|
2593
|
-
* a report, never branch on it.
|
|
2594
|
-
*
|
|
2595
|
-
* @example
|
|
2596
|
-
* ```ts
|
|
2597
|
-
* import { ScaffoldError, isScaffoldError } from '@orkestrel/scaffold'
|
|
2598
|
-
*
|
|
2599
|
-
* try {
|
|
2600
|
-
* throw new ScaffoldError('INVALID', 'Blueprint is not an exact record')
|
|
2601
|
-
* } catch (error) {
|
|
2602
|
-
* if (isScaffoldError(error)) error.code // 'INVALID'
|
|
2603
|
-
* }
|
|
2604
|
-
* ```
|
|
2605
|
-
*/
|
|
2606
|
-
export declare class ScaffoldError extends Error {
|
|
2607
|
-
readonly code: ScaffoldErrorCode;
|
|
2608
|
-
readonly context?: unknown;
|
|
2609
|
-
/**
|
|
2610
|
-
* Construct a coded scaffold error.
|
|
2611
|
-
*
|
|
2612
|
-
* @param code - The coded reason the error is raised.
|
|
2613
|
-
* @param message - What went wrong, in one sentence.
|
|
2614
|
-
* @param context - Whatever the raising site can say about the failure.
|
|
2615
|
-
*/
|
|
2616
|
-
constructor(code: ScaffoldErrorCode, message: string, context?: unknown);
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
/** The coded reasons a scaffold error is raised. */
|
|
2620
|
-
export declare type ScaffoldErrorCode = 'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH';
|
|
2621
|
-
|
|
2622
|
-
/**
|
|
2623
|
-
* The replayable outcome of one compile.
|
|
2624
|
-
*
|
|
2625
|
-
* @remarks
|
|
2626
|
-
* `plan` is present exactly when the compile completed, so it is also the
|
|
2627
|
-
* completeness test, and it carries the blueprint it was compiled from. A
|
|
2628
|
-
* gated compile returns the questions that closed the gate and the stage
|
|
2629
|
-
* records up to it, and no plan; the caller still holds the blueprint it
|
|
2630
|
-
* passed in, so repeating it here would be one fact stored twice and free to
|
|
2631
|
-
* disagree with itself.
|
|
2632
|
-
*/
|
|
2633
|
-
export declare interface Scaffolding {
|
|
2634
|
-
readonly plan?: Plan;
|
|
2635
|
-
readonly questions: readonly Question[];
|
|
2636
|
-
readonly stages: readonly CompileRecord[];
|
|
2637
|
-
readonly blueprint?: never;
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
/**
|
|
2641
|
-
* Select the groups a compile covers, in plan order.
|
|
2642
|
-
*
|
|
2643
|
-
* @param groups - The requested selection; every group when absent.
|
|
2644
|
-
* @returns The requested groups in `GROUPS` order, without repeats.
|
|
2645
|
-
*
|
|
2646
|
-
* @remarks
|
|
2647
|
-
* A caller's selection is data, so it arrives in whatever order and with
|
|
2648
|
-
* whatever repeats the caller wrote. Plan order is this package's, so the
|
|
2649
|
-
* selection is read as membership and the order comes from `GROUPS`. An empty
|
|
2650
|
-
* selection covers nothing, which is a caller asking for an empty plan rather
|
|
2651
|
-
* than a caller asking for everything.
|
|
2652
|
-
*
|
|
2653
|
-
* @example
|
|
2654
|
-
* ```ts
|
|
2655
|
-
* import { selectGroups } from '@orkestrel/scaffold'
|
|
2656
|
-
*
|
|
2657
|
-
* selectGroups(['tests', 'manifest', 'tests']) // ['manifest', 'tests']
|
|
2658
|
-
* selectGroups() // every group, in plan order
|
|
2659
|
-
* ```
|
|
2660
|
-
*/
|
|
2661
|
-
export declare function selectGroups(groups?: readonly Group[]): readonly Group[];
|
|
2363
|
+
/**
|
|
2364
|
+
* Coerce an untrusted value to a {@link Snapshot}.
|
|
2365
|
+
*
|
|
2366
|
+
* @param value - The value to parse.
|
|
2367
|
+
* @returns The snapshot, or `undefined` when the value is not one.
|
|
2368
|
+
*
|
|
2369
|
+
* @remarks
|
|
2370
|
+
* Derived from {@link isSnapshot}.
|
|
2371
|
+
*
|
|
2372
|
+
* @example
|
|
2373
|
+
* ```ts
|
|
2374
|
+
* import { parseSnapshot } from '@orkestrel/scaffold'
|
|
2375
|
+
*
|
|
2376
|
+
* parseSnapshot({ 'AGENTS.md': '68690a' }) // { 'AGENTS.md': '68690a' }
|
|
2377
|
+
* parseSnapshot({ 'AGENTS.md': null }) // undefined
|
|
2378
|
+
* ```
|
|
2379
|
+
*/
|
|
2380
|
+
export declare function parseSnapshot(value: unknown): Snapshot | undefined;
|
|
2662
2381
|
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2382
|
+
/**
|
|
2383
|
+
* Build one `exports` condition block for a built environment.
|
|
2384
|
+
*
|
|
2385
|
+
* @param path - The extensionless `dist` path both conditions point at.
|
|
2386
|
+
* @param formats - The module formats that environment builds.
|
|
2387
|
+
* @returns The condition block: an `import` condition always, and a `require`
|
|
2388
|
+
* condition only where a CommonJS build exists.
|
|
2389
|
+
*
|
|
2390
|
+
* @remarks
|
|
2391
|
+
* The formats decide the shape, so no caller repeats the rule. An environment
|
|
2392
|
+
* that builds ES only publishes an `import` condition alone rather than a
|
|
2393
|
+
* `default` one, because a `default` condition answers `require` too and would
|
|
2394
|
+
* hand a CommonJS consumer a module its loader cannot read.
|
|
2395
|
+
*
|
|
2396
|
+
* @example
|
|
2397
|
+
* ```ts
|
|
2398
|
+
* import { pathToCondition } from '@orkestrel/scaffold'
|
|
2399
|
+
*
|
|
2400
|
+
* pathToCondition('./dist/src/browser/index', ['es'])
|
|
2401
|
+
* // { import: { types: './dist/src/browser/index.d.ts', default: './dist/src/browser/index.js' } }
|
|
2402
|
+
* ```
|
|
2403
|
+
*/
|
|
2404
|
+
export declare function pathToCondition(path: string, formats: readonly BuildFormat[]): Readonly<Record<string, unknown>>;
|
|
2683
2405
|
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2406
|
+
/**
|
|
2407
|
+
* The compiled, ordered artifact list and the selection it covers.
|
|
2408
|
+
*
|
|
2409
|
+
* @remarks
|
|
2410
|
+
* `hash` is the plan's content identity and is absent until the pin stage
|
|
2411
|
+
* fills it.
|
|
2412
|
+
*/
|
|
2413
|
+
export declare interface Plan {
|
|
2414
|
+
readonly blueprint: Blueprint;
|
|
2415
|
+
readonly groups: readonly Group[];
|
|
2416
|
+
readonly artifacts: readonly Artifact[];
|
|
2417
|
+
readonly hash?: string;
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
/** The tally of one plan by artifact origin. */
|
|
2421
|
+
export declare interface PlanSummary {
|
|
2422
|
+
readonly name: string;
|
|
2423
|
+
readonly src: readonly Environment[];
|
|
2424
|
+
readonly app: readonly Environment[];
|
|
2425
|
+
readonly groups: readonly Group[];
|
|
2426
|
+
readonly host: number;
|
|
2427
|
+
readonly template: number;
|
|
2428
|
+
readonly computed: number;
|
|
2429
|
+
}
|
|
2704
2430
|
|
|
2705
|
-
|
|
2706
|
-
|
|
2431
|
+
/**
|
|
2432
|
+
* Compare a plan against a target's current content.
|
|
2433
|
+
*
|
|
2434
|
+
* @param plan - The compiled plan.
|
|
2435
|
+
* @param current - The target's exact bytes, keyed by artifact-relative path.
|
|
2436
|
+
* @returns One finding per planned artifact in plan order, then one `foreign`
|
|
2437
|
+
* finding per unplanned path in snapshot order.
|
|
2438
|
+
*
|
|
2439
|
+
* @remarks
|
|
2440
|
+
* The sweep is bounded by the plan's own selection: a path the plan does not own
|
|
2441
|
+
* is reported as foreign only when its group is one the plan covers, so a
|
|
2442
|
+
* compile narrowed to a few groups never reports the rest of the workspace as
|
|
2443
|
+
* unowned. Within a covered group the report is deliberately wide, because
|
|
2444
|
+
* `foreign` is the set the destructive verb draws from and the narrowing that
|
|
2445
|
+
* set needs — the paths no verb may remove, and what git tracks — belongs to
|
|
2446
|
+
* that verb rather than to the comparison.
|
|
2447
|
+
*
|
|
2448
|
+
* @example
|
|
2449
|
+
* ```ts
|
|
2450
|
+
* import { Compiler, createBlueprint, planToFindings } from '@orkestrel/scaffold'
|
|
2451
|
+
*
|
|
2452
|
+
* const { plan } = new Compiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2453
|
+
*
|
|
2454
|
+
* plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
|
|
2455
|
+
* ```
|
|
2456
|
+
*/
|
|
2457
|
+
export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
|
|
2707
2458
|
|
|
2708
|
-
|
|
2709
|
-
|
|
2459
|
+
/**
|
|
2460
|
+
* Compute a plan's content identity.
|
|
2461
|
+
*
|
|
2462
|
+
* @param plan - The plan to identify.
|
|
2463
|
+
* @returns Sixteen lowercase hexadecimal digits, or `undefined` when the plan
|
|
2464
|
+
* carries a value JSON cannot encode.
|
|
2465
|
+
*
|
|
2466
|
+
* @remarks
|
|
2467
|
+
* The identity covers the blueprint the plan was compiled from, the groups it
|
|
2468
|
+
* covers, and its ordered artifacts. It deliberately excludes `hash` itself,
|
|
2469
|
+
* which is what lets a pinned plan be re-identified and compared without
|
|
2470
|
+
* stripping a field first.
|
|
2471
|
+
*
|
|
2472
|
+
* The projection is canonical, so two plans that differ only in key order
|
|
2473
|
+
* answer the same digits. Nothing here reads a clock or randomness, and the
|
|
2474
|
+
* refusal is total: a value that cannot be read answers `undefined` rather than
|
|
2475
|
+
* escaping as a thrown error, and a caller decides what an unidentifiable plan
|
|
2476
|
+
* means.
|
|
2477
|
+
*
|
|
2478
|
+
* @example
|
|
2479
|
+
* ```ts
|
|
2480
|
+
* import { Compiler, createBlueprint, planToHash } from '@orkestrel/scaffold'
|
|
2481
|
+
*
|
|
2482
|
+
* const { plan } = new Compiler().compile(createBlueprint('router', { src: ['core'] }))
|
|
2483
|
+
*
|
|
2484
|
+
* plan === undefined ? undefined : planToHash(plan)?.length // 16
|
|
2485
|
+
* ```
|
|
2486
|
+
*/
|
|
2487
|
+
export declare function planToHash(plan: Plan): string | undefined;
|
|
2710
2488
|
|
|
2711
|
-
|
|
2712
|
-
|
|
2489
|
+
/**
|
|
2490
|
+
* Project a plan into its tally by artifact origin.
|
|
2491
|
+
*
|
|
2492
|
+
* @param plan - The plan to summarize.
|
|
2493
|
+
* @returns The workspace's name, both environment axes, the covered groups, and
|
|
2494
|
+
* one count per origin.
|
|
2495
|
+
*
|
|
2496
|
+
* @remarks
|
|
2497
|
+
* Lossy on purpose: the summary is what a report prints, and it holds nothing a
|
|
2498
|
+
* caller could mistake for the plan itself. The counts are derived on each call
|
|
2499
|
+
* rather than stored on the plan, so they cannot disagree with the artifacts
|
|
2500
|
+
* beside them.
|
|
2501
|
+
*
|
|
2502
|
+
* @example
|
|
2503
|
+
* ```ts
|
|
2504
|
+
* import { planToSummary } from '@orkestrel/scaffold'
|
|
2505
|
+
*
|
|
2506
|
+
* planToSummary(plan).computed // the number of computed artifacts
|
|
2507
|
+
* ```
|
|
2508
|
+
*/
|
|
2509
|
+
export declare function planToSummary(plan: Plan): PlanSummary;
|
|
2713
2510
|
|
|
2714
|
-
|
|
2715
|
-
|
|
2511
|
+
/** Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. */
|
|
2512
|
+
export declare const PRINT_WIDTH = 100;
|
|
2716
2513
|
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2514
|
+
/**
|
|
2515
|
+
* One validation issue raised against a blueprint or a plan.
|
|
2516
|
+
*
|
|
2517
|
+
* @remarks
|
|
2518
|
+
* A blocking question fails the gate closed. A non-blocking question is an
|
|
2519
|
+
* advisory that rides a complete result. `candidates` names the accepted
|
|
2520
|
+
* values when the issue is a rejected choice.
|
|
2521
|
+
*/
|
|
2522
|
+
export declare interface Question {
|
|
2523
|
+
readonly field: string;
|
|
2524
|
+
readonly message: string;
|
|
2525
|
+
readonly blocking: boolean;
|
|
2526
|
+
readonly candidates?: readonly string[];
|
|
2527
|
+
}
|
|
2728
2528
|
|
|
2729
|
-
|
|
2730
|
-
|
|
2529
|
+
/**
|
|
2530
|
+
* One declared dependency range measured against the registry's latest release.
|
|
2531
|
+
*
|
|
2532
|
+
* @remarks
|
|
2533
|
+
* A found lookup carries the version upstream reported; one that produced no
|
|
2534
|
+
* answer carries the cause and no version. Whether the declared range already
|
|
2535
|
+
* admits that version is not recorded, because it is a deterministic function
|
|
2536
|
+
* of `range` and `latest`: a stored answer could only disagree with the
|
|
2537
|
+
* fields beside it. One centralized helper decides it, and every caller reads
|
|
2538
|
+
* the same decision.
|
|
2539
|
+
*/
|
|
2540
|
+
export declare type Release = {
|
|
2541
|
+
readonly name: string;
|
|
2542
|
+
readonly range: string;
|
|
2543
|
+
readonly lookup: 'found';
|
|
2544
|
+
readonly latest: string;
|
|
2545
|
+
readonly note?: never;
|
|
2546
|
+
} | {
|
|
2547
|
+
readonly name: string;
|
|
2548
|
+
readonly range: string;
|
|
2549
|
+
readonly lookup: 'missing' | 'failed';
|
|
2550
|
+
readonly note: string;
|
|
2551
|
+
readonly latest?: never;
|
|
2552
|
+
};
|
|
2731
2553
|
|
|
2732
|
-
|
|
2733
|
-
|
|
2554
|
+
/**
|
|
2555
|
+
* The one error this package throws, carrying the coded reason it was raised.
|
|
2556
|
+
*
|
|
2557
|
+
* @remarks
|
|
2558
|
+
* Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
|
|
2559
|
+
* any call made after teardown, `TARGET` for a destination that is not what the
|
|
2560
|
+
* caller's observation said it was, `WRITE` for a mutation that could not be
|
|
2561
|
+
* completed, `FETCH` for an upstream read that produced no answer the caller can
|
|
2562
|
+
* be given, and `BLOCKED` for a refused blueprint.
|
|
2563
|
+
*
|
|
2564
|
+
* `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
|
|
2565
|
+
* — this blueprint will not be built — and the questions say which. The compiler
|
|
2566
|
+
* answers its refusal rather than throwing it: the gate fails closed, returns the
|
|
2567
|
+
* questions that closed it, and records `BLOCKED` on its stage, so a caller reads
|
|
2568
|
+
* that refusal from the value it asked for. A verb that creates a workspace
|
|
2569
|
+
* throws it, because it chose the shape and has nothing to hand back. A blocking
|
|
2570
|
+
* question closed the gate; a non-blocking one is a shape this package can
|
|
2571
|
+
* describe and declines to create.
|
|
2572
|
+
*
|
|
2573
|
+
* `context` carries whatever the raising site can say about the failure. It is
|
|
2574
|
+
* `unknown` because nothing narrows it usefully at the catch site; read it for
|
|
2575
|
+
* a report, never branch on it.
|
|
2576
|
+
*
|
|
2577
|
+
* @example
|
|
2578
|
+
* ```ts
|
|
2579
|
+
* import { ScaffoldError, isScaffoldError } from '@orkestrel/scaffold'
|
|
2580
|
+
*
|
|
2581
|
+
* try {
|
|
2582
|
+
* throw new ScaffoldError('INVALID', 'Blueprint is not an exact record')
|
|
2583
|
+
* } catch (error) {
|
|
2584
|
+
* if (isScaffoldError(error)) error.code // 'INVALID'
|
|
2585
|
+
* }
|
|
2586
|
+
* ```
|
|
2587
|
+
*/
|
|
2588
|
+
export declare class ScaffoldError extends Error {
|
|
2589
|
+
readonly code: ScaffoldErrorCode;
|
|
2590
|
+
readonly context?: unknown;
|
|
2591
|
+
/**
|
|
2592
|
+
* Construct a coded scaffold error.
|
|
2593
|
+
*
|
|
2594
|
+
* @param code - The coded reason the error is raised.
|
|
2595
|
+
* @param message - What went wrong, in one sentence.
|
|
2596
|
+
* @param context - Whatever the raising site can say about the failure.
|
|
2597
|
+
*/
|
|
2598
|
+
constructor(code: ScaffoldErrorCode, message: string, context?: unknown);
|
|
2599
|
+
}
|
|
2734
2600
|
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
*
|
|
2738
|
-
* @remarks
|
|
2739
|
-
* Per environment: the thin configuration files it adds under `configs/src`,
|
|
2740
|
-
* its Vitest project label, its `exports` subpath, and the module formats it
|
|
2741
|
-
* builds. Core alone occupies the package root, so it is the only environment
|
|
2742
|
-
* whose subpath is `.`; browser ships ES only because no CommonJS consumer
|
|
2743
|
-
* reaches it.
|
|
2744
|
-
*/
|
|
2745
|
-
export declare const SRC_MATRIX: Readonly<Record<Environment, SrcDefinition>>;
|
|
2746
|
-
|
|
2747
|
-
/** The build and export settings one published `src` environment contributes. */
|
|
2748
|
-
export declare interface SrcDefinition {
|
|
2749
|
-
readonly configs: readonly string[];
|
|
2750
|
-
readonly project: string;
|
|
2751
|
-
readonly path: string;
|
|
2752
|
-
readonly formats: readonly BuildFormat[];
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
|
-
/**
|
|
2756
|
-
* Project a published selection into the manifest's entry fields.
|
|
2757
|
-
*
|
|
2758
|
-
* @param src - The declared published environments.
|
|
2759
|
-
* @returns The `main` and `module` fields, plus `types` when one environment
|
|
2760
|
-
* owns the package root.
|
|
2761
|
-
*
|
|
2762
|
-
* @remarks
|
|
2763
|
-
* `main` follows the root environment's own formats, so an environment that
|
|
2764
|
-
* builds ES only points both fields at the same file rather than promising a
|
|
2765
|
-
* CommonJS build that never runs. A selection with several environments carries
|
|
2766
|
-
* no top-level `types`, because each environment declares its own under its
|
|
2767
|
-
* subpath and a single top-level field could only name one of them.
|
|
2768
|
-
*
|
|
2769
|
-
* @example
|
|
2770
|
-
* ```ts
|
|
2771
|
-
* import { srcToEntry } from '@orkestrel/scaffold'
|
|
2772
|
-
*
|
|
2773
|
-
* srcToEntry(['core']).main // './dist/src/core/index.cjs'
|
|
2774
|
-
* srcToEntry(['browser']).main // './dist/src/browser/index.js'
|
|
2775
|
-
* ```
|
|
2776
|
-
*/
|
|
2777
|
-
export declare function srcToEntry(src: readonly Environment[]): {
|
|
2778
|
-
readonly main: string;
|
|
2779
|
-
readonly module: string;
|
|
2780
|
-
readonly types?: string;
|
|
2781
|
-
};
|
|
2782
|
-
|
|
2783
|
-
/**
|
|
2784
|
-
* Project a published selection into the manifest's `exports` map.
|
|
2785
|
-
*
|
|
2786
|
-
* @param src - The declared published environments.
|
|
2787
|
-
* @returns The map, keyed by subpath in `ENVIRONMENTS` order.
|
|
2788
|
-
*
|
|
2789
|
-
* @remarks
|
|
2790
|
-
* One environment owns the package root and every other declared environment
|
|
2791
|
-
* takes the subpath its `SRC_MATRIX` row names, so the map never invents a
|
|
2792
|
-
* subpath. `./package.json` is published alongside, which is what lets a
|
|
2793
|
-
* consumer's tooling read the manifest of a package whose exports are otherwise
|
|
2794
|
-
* closed. A selection publishing nothing answers an empty map rather than a
|
|
2795
|
-
* core-rooted one, because a workspace with no published environment declares
|
|
2796
|
-
* no exports at all.
|
|
2797
|
-
*
|
|
2798
|
-
* @example
|
|
2799
|
-
* ```ts
|
|
2800
|
-
* import { srcToExports } from '@orkestrel/scaffold'
|
|
2801
|
-
*
|
|
2802
|
-
* Object.keys(srcToExports(['core', 'server'])) // ['.', './server', './package.json']
|
|
2803
|
-
* srcToExports([]) // {}
|
|
2804
|
-
* ```
|
|
2805
|
-
*/
|
|
2806
|
-
export declare function srcToExports(src: readonly Environment[]): Readonly<Record<string, unknown>>;
|
|
2601
|
+
/** The coded reasons a scaffold error is raised. */
|
|
2602
|
+
export declare type ScaffoldErrorCode = 'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH';
|
|
2807
2603
|
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
* ```ts
|
|
2825
|
-
* import { srcToRoot } from '@orkestrel/scaffold'
|
|
2826
|
-
*
|
|
2827
|
-
* srcToRoot(['browser']) // 'browser'
|
|
2828
|
-
* srcToRoot(['core', 'server']) // undefined
|
|
2829
|
-
* ```
|
|
2830
|
-
*/
|
|
2831
|
-
export declare function srcToRoot(src: readonly Environment[]): Environment | undefined;
|
|
2604
|
+
/**
|
|
2605
|
+
* The replayable outcome of one compile.
|
|
2606
|
+
*
|
|
2607
|
+
* @remarks
|
|
2608
|
+
* `plan` is present exactly when the compile completed, so it is also the
|
|
2609
|
+
* completeness test, and `scaffolding.plan.blueprint` carries the blueprint it
|
|
2610
|
+
* was compiled from. A gated compile returns the questions that closed the gate
|
|
2611
|
+
* and the stage records up to it, and no plan; the caller still holds the
|
|
2612
|
+
* blueprint it passed in, so repeating it at this level would be one fact stored
|
|
2613
|
+
* twice and free to disagree with itself.
|
|
2614
|
+
*/
|
|
2615
|
+
export declare interface Scaffolding {
|
|
2616
|
+
readonly plan?: Plan;
|
|
2617
|
+
readonly questions: readonly Question[];
|
|
2618
|
+
readonly stages: readonly CompileRecord[];
|
|
2619
|
+
}
|
|
2832
2620
|
|
|
2833
|
-
|
|
2834
|
-
|
|
2621
|
+
/**
|
|
2622
|
+
* Select the groups a compile covers, in plan order.
|
|
2623
|
+
*
|
|
2624
|
+
* @param groups - The requested selection; every group when absent.
|
|
2625
|
+
* @returns The requested groups in `GROUPS` order, without repeats.
|
|
2626
|
+
*
|
|
2627
|
+
* @remarks
|
|
2628
|
+
* A caller's selection is data, so it arrives in whatever order and with
|
|
2629
|
+
* whatever repeats the caller wrote. Plan order is this package's, so the
|
|
2630
|
+
* selection is read as membership and the order comes from `GROUPS`. An empty
|
|
2631
|
+
* selection covers nothing, which is a caller asking for an empty plan rather
|
|
2632
|
+
* than a caller asking for everything.
|
|
2633
|
+
*
|
|
2634
|
+
* @example
|
|
2635
|
+
* ```ts
|
|
2636
|
+
* import { selectGroups } from '@orkestrel/scaffold'
|
|
2637
|
+
*
|
|
2638
|
+
* selectGroups(['tests', 'manifest', 'tests']) // ['manifest', 'tests']
|
|
2639
|
+
* selectGroups() // every group, in plan order
|
|
2640
|
+
* ```
|
|
2641
|
+
*/
|
|
2642
|
+
export declare function selectGroups(groups?: readonly Group[]): readonly Group[];
|
|
2835
2643
|
|
|
2836
|
-
|
|
2837
|
-
|
|
2644
|
+
/**
|
|
2645
|
+
* Select the host paths a named workspace vendors.
|
|
2646
|
+
*
|
|
2647
|
+
* @param paths - The candidate host paths, in their declared order.
|
|
2648
|
+
* @param name - The target workspace's own bare package name.
|
|
2649
|
+
* @returns Every candidate except the workspace's own guide, in input order.
|
|
2650
|
+
*
|
|
2651
|
+
* @remarks
|
|
2652
|
+
* `HOST_PATHS` is a candidate set rather than a plan, because a workspace never
|
|
2653
|
+
* mirrors its own guide: that file is the workspace's own product, and vendoring
|
|
2654
|
+
* it would have the target overwrite its guide with the copy it published.
|
|
2655
|
+
*
|
|
2656
|
+
* @example
|
|
2657
|
+
* ```ts
|
|
2658
|
+
* import { HOST_PATHS, selectHostPaths } from '@orkestrel/scaffold'
|
|
2659
|
+
*
|
|
2660
|
+
* selectHostPaths(HOST_PATHS, 'scaffold').includes('guides/scaffold.md') // false
|
|
2661
|
+
* ```
|
|
2662
|
+
*/
|
|
2663
|
+
export declare function selectHostPaths(paths: readonly string[], name: string): readonly string[];
|
|
2838
2664
|
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2665
|
+
/**
|
|
2666
|
+
* Serialize one string as a single-quoted TypeScript literal.
|
|
2667
|
+
*
|
|
2668
|
+
* @param value - The string to serialize.
|
|
2669
|
+
* @returns A complete single-quoted literal with line-breaking and delimiter
|
|
2670
|
+
* characters escaped.
|
|
2671
|
+
*
|
|
2672
|
+
* @remarks
|
|
2673
|
+
* Configuration templates insert blueprint-derived strings into TypeScript.
|
|
2674
|
+
* Keeping this one serializer at that boundary prevents a name from becoming
|
|
2675
|
+
* syntax and preserves oxfmt's configured single-quote fixed point.
|
|
2676
|
+
*
|
|
2677
|
+
* @example
|
|
2678
|
+
* ```ts
|
|
2679
|
+
* import { serializeTypeScriptString } from '@orkestrel/scaffold'
|
|
2680
|
+
*
|
|
2681
|
+
* serializeTypeScriptString("it's") // `'it\\'s'`
|
|
2682
|
+
* ```
|
|
2683
|
+
*/
|
|
2684
|
+
export declare function serializeTypeScriptString(value: string): string;
|
|
2685
|
+
|
|
2686
|
+
/** The provisioner skeleton a workspace with declared service vendors is given once. */
|
|
2687
|
+
export declare const SERVICE_SCRIPT_PATH = "scripts/service.sh";
|
|
2688
|
+
|
|
2689
|
+
/** The live-service readiness module whose presence makes a workspace `service`. */
|
|
2690
|
+
export declare const SERVICE_SETUP_PATH = "tests/setupService.ts";
|
|
2691
|
+
|
|
2692
|
+
/** The include the live-service project covers, which is a directory rather than one proof. */
|
|
2693
|
+
export declare const SERVICE_TEST_INCLUDE = "tests/service/**/*.test.ts";
|
|
2694
|
+
|
|
2695
|
+
/** The Vite wrapper whose presence makes a workspace `showcase`. */
|
|
2696
|
+
export declare const SHOWCASE_CONFIG_PATH = "configs/app/vite.showcase.config.ts";
|
|
2697
|
+
|
|
2698
|
+
/**
|
|
2699
|
+
* The development dependency used only by the optional single-file showcase build.
|
|
2700
|
+
*
|
|
2701
|
+
* @example
|
|
2702
|
+
* ```ts
|
|
2703
|
+
* import { SHOWCASE_DEV_DEPENDENCIES } from '@orkestrel/scaffold'
|
|
2704
|
+
*
|
|
2705
|
+
* SHOWCASE_DEV_DEPENDENCIES['vite-plugin-singlefile'] // '^2.3.3'
|
|
2706
|
+
* ```
|
|
2707
|
+
*/
|
|
2708
|
+
export declare const SHOWCASE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
2709
|
+
|
|
2710
|
+
/** Exact lowercase hexadecimal target bytes keyed by artifact-relative path. */
|
|
2711
|
+
export declare type Snapshot = Readonly<Record<string, string>>;
|
|
2712
|
+
|
|
2713
|
+
/** The development dependencies a published browser `src` environment adds. */
|
|
2714
|
+
export declare const SOURCE_BROWSER_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
2715
|
+
|
|
2716
|
+
/**
|
|
2717
|
+
* The build and export settings each published `src` environment contributes, frozen.
|
|
2718
|
+
*
|
|
2719
|
+
* @remarks
|
|
2720
|
+
* Per environment: the thin configuration files it adds under `configs/src`,
|
|
2721
|
+
* its Vitest project label, its `exports` subpath, and the module formats it
|
|
2722
|
+
* builds. Core alone occupies the package root, so it is the only environment
|
|
2723
|
+
* whose subpath is `.`; browser ships ES only because no CommonJS consumer
|
|
2724
|
+
* reaches it.
|
|
2725
|
+
*/
|
|
2726
|
+
export declare const SRC_MATRIX: Readonly<Record<Environment, SrcDefinition>>;
|
|
2727
|
+
|
|
2728
|
+
/** The build and export settings one published `src` environment contributes. */
|
|
2729
|
+
export declare interface SrcDefinition {
|
|
2730
|
+
readonly configs: readonly string[];
|
|
2731
|
+
readonly project: string;
|
|
2732
|
+
readonly path: string;
|
|
2733
|
+
readonly formats: readonly BuildFormat[];
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
/**
|
|
2737
|
+
* Project a published selection into the manifest's entry fields.
|
|
2738
|
+
*
|
|
2739
|
+
* @param src - The declared published environments.
|
|
2740
|
+
* @returns The `main` and `module` fields, plus `types` when one environment
|
|
2741
|
+
* owns the package root.
|
|
2742
|
+
*
|
|
2743
|
+
* @remarks
|
|
2744
|
+
* `main` follows the root environment's own formats, so an environment that
|
|
2745
|
+
* builds ES only points both fields at the same file rather than promising a
|
|
2746
|
+
* CommonJS build that never runs. A selection with several environments carries
|
|
2747
|
+
* no top-level `types`, because each environment declares its own under its
|
|
2748
|
+
* subpath and a single top-level field could only name one of them.
|
|
2749
|
+
*
|
|
2750
|
+
* @example
|
|
2751
|
+
* ```ts
|
|
2752
|
+
* import { srcToEntry } from '@orkestrel/scaffold'
|
|
2753
|
+
*
|
|
2754
|
+
* srcToEntry(['core']).main // './dist/src/core/index.cjs'
|
|
2755
|
+
* srcToEntry(['browser']).main // './dist/src/browser/index.js'
|
|
2756
|
+
* ```
|
|
2757
|
+
*/
|
|
2758
|
+
export declare function srcToEntry(src: readonly Environment[]): {
|
|
2759
|
+
readonly main: string;
|
|
2760
|
+
readonly module: string;
|
|
2761
|
+
readonly types?: string;
|
|
2762
|
+
};
|
|
2763
|
+
|
|
2764
|
+
/**
|
|
2765
|
+
* Project a published selection into the manifest's `exports` map.
|
|
2766
|
+
*
|
|
2767
|
+
* @param src - The declared published environments.
|
|
2768
|
+
* @returns The map, keyed by subpath in `ENVIRONMENTS` order.
|
|
2769
|
+
*
|
|
2770
|
+
* @remarks
|
|
2771
|
+
* One environment owns the package root and every other declared environment
|
|
2772
|
+
* takes the subpath its `SRC_MATRIX` row names, so the map never invents a
|
|
2773
|
+
* subpath. `./package.json` is published alongside, which is what lets a
|
|
2774
|
+
* consumer's tooling read the manifest of a package whose exports are otherwise
|
|
2775
|
+
* closed. A selection publishing nothing answers an empty map rather than a
|
|
2776
|
+
* core-rooted one, because a workspace with no published environment declares
|
|
2777
|
+
* no exports at all.
|
|
2778
|
+
*
|
|
2779
|
+
* @example
|
|
2780
|
+
* ```ts
|
|
2781
|
+
* import { srcToExports } from '@orkestrel/scaffold'
|
|
2782
|
+
*
|
|
2783
|
+
* Object.keys(srcToExports(['core', 'server'])) // ['.', './server', './package.json']
|
|
2784
|
+
* srcToExports([]) // {}
|
|
2785
|
+
* ```
|
|
2786
|
+
*/
|
|
2787
|
+
export declare function srcToExports(src: readonly Environment[]): Readonly<Record<string, unknown>>;
|
|
2788
|
+
|
|
2789
|
+
/**
|
|
2790
|
+
* Select the single published environment a package root points at.
|
|
2791
|
+
*
|
|
2792
|
+
* @param src - The declared published environments.
|
|
2793
|
+
* @returns That environment, or `undefined` when the selection declares none or
|
|
2794
|
+
* several.
|
|
2795
|
+
*
|
|
2796
|
+
* @remarks
|
|
2797
|
+
* A workspace publishing exactly one environment puts it at the package root,
|
|
2798
|
+
* so its entry fields and its `'.'` export condition both name that
|
|
2799
|
+
* environment's build. A workspace publishing several puts core at the root and
|
|
2800
|
+
* gives every other environment a subpath, so there is no single root to name.
|
|
2801
|
+
* Both callers read the same answer, which is why the branch is decided once
|
|
2802
|
+
* here rather than twice.
|
|
2803
|
+
*
|
|
2804
|
+
* @example
|
|
2805
|
+
* ```ts
|
|
2806
|
+
* import { srcToRoot } from '@orkestrel/scaffold'
|
|
2807
|
+
*
|
|
2808
|
+
* srcToRoot(['browser']) // 'browser'
|
|
2809
|
+
* srcToRoot(['core', 'server']) // undefined
|
|
2810
|
+
* ```
|
|
2811
|
+
*/
|
|
2812
|
+
export declare function srcToRoot(src: readonly Environment[]): Environment | undefined;
|
|
2813
|
+
|
|
2814
|
+
/** Columns one tab occupies when the formatter measures a line, matching `tabWidth`. */
|
|
2815
|
+
export declare const TAB_WIDTH = 2;
|
|
2816
|
+
|
|
2817
|
+
/** The exact `major.minor.patch` version syntax a blueprint declares. */
|
|
2818
|
+
export declare const VERSION_PATTERN: RegExp;
|
|
2819
|
+
|
|
2820
|
+
/**
|
|
2821
|
+
* Which host-specific pipelines a generated root Vite configuration carries.
|
|
2822
|
+
*
|
|
2823
|
+
* @remarks
|
|
2824
|
+
* Boundary guarantees never vary by blueprint, so they are not selected here:
|
|
2825
|
+
* every generated configuration emits the environment-boundary plugin, its
|
|
2826
|
+
* module-graph audit, and stylesheet rejection. `browser` selects the shared
|
|
2827
|
+
* root CSS analysis and real-browser test machinery. `vue` selects the
|
|
2828
|
+
* single-file-component, HTML, and development-server machinery an application
|
|
2829
|
+
* browser environment needs. `output` selects build-output containment.
|
|
2830
|
+
* `showcase` selects the optional single-file application-browser projection.
|
|
2831
|
+
*/
|
|
2832
|
+
export declare interface ViteMachinery {
|
|
2833
|
+
readonly browser: boolean;
|
|
2834
|
+
readonly vue: boolean;
|
|
2835
|
+
readonly output: boolean;
|
|
2836
|
+
readonly showcase: boolean;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
/**
|
|
2840
|
+
* The vendored paths whose present bytes belong to each workspace, frozen.
|
|
2841
|
+
*
|
|
2842
|
+
* @remarks
|
|
2843
|
+
* These paths are copied into a workspace when absent and are never compared
|
|
2844
|
+
* or replaced while present. A workspace therefore stops receiving later
|
|
2845
|
+
* canonical updates to them. `.gitignore` takes that trade because its correct
|
|
2846
|
+
* rules differ by workspace, so scaffold cannot own its bytes.
|
|
2847
|
+
*/
|
|
2848
|
+
export declare const WORKSPACE_OWNED_PATHS: readonly string[];
|
|
2868
2849
|
|
|
2869
|
-
|
|
2850
|
+
export { }
|