@davidorex/pi-context 0.26.0
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/CHANGELOG.md +23 -0
- package/README.md +195 -0
- package/dist/block-api.d.ts +328 -0
- package/dist/block-api.d.ts.map +1 -0
- package/dist/block-api.js +1167 -0
- package/dist/block-api.js.map +1 -0
- package/dist/block-validation.d.ts +33 -0
- package/dist/block-validation.d.ts.map +1 -0
- package/dist/block-validation.js +213 -0
- package/dist/block-validation.js.map +1 -0
- package/dist/context-dir.d.ts +93 -0
- package/dist/context-dir.d.ts.map +1 -0
- package/dist/context-dir.js +224 -0
- package/dist/context-dir.js.map +1 -0
- package/dist/context-sdk.d.ts +428 -0
- package/dist/context-sdk.d.ts.map +1 -0
- package/dist/context-sdk.js +1327 -0
- package/dist/context-sdk.js.map +1 -0
- package/dist/context.d.ts +497 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +1060 -0
- package/dist/context.js.map +1 -0
- package/dist/dispatch-context.d.ts +112 -0
- package/dist/dispatch-context.d.ts.map +1 -0
- package/dist/dispatch-context.js +119 -0
- package/dist/dispatch-context.js.map +1 -0
- package/dist/execution-context.d.ts +138 -0
- package/dist/execution-context.d.ts.map +1 -0
- package/dist/execution-context.js +151 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1655 -0
- package/dist/index.js.map +1 -0
- package/dist/lens-validator.d.ts +50 -0
- package/dist/lens-validator.d.ts.map +1 -0
- package/dist/lens-validator.js +46 -0
- package/dist/lens-validator.js.map +1 -0
- package/dist/lens-view.d.ts +75 -0
- package/dist/lens-view.d.ts.map +1 -0
- package/dist/lens-view.js +253 -0
- package/dist/lens-view.js.map +1 -0
- package/dist/rename-canonical-id.d.ts +64 -0
- package/dist/rename-canonical-id.d.ts.map +1 -0
- package/dist/rename-canonical-id.js +378 -0
- package/dist/rename-canonical-id.js.map +1 -0
- package/dist/roadmap-plan.d.ts +186 -0
- package/dist/roadmap-plan.d.ts.map +1 -0
- package/dist/roadmap-plan.js +534 -0
- package/dist/roadmap-plan.js.map +1 -0
- package/dist/samples-catalog.d.ts +48 -0
- package/dist/samples-catalog.d.ts.map +1 -0
- package/dist/samples-catalog.js +128 -0
- package/dist/samples-catalog.js.map +1 -0
- package/dist/schema-migrations.d.ts +82 -0
- package/dist/schema-migrations.d.ts.map +1 -0
- package/dist/schema-migrations.js +106 -0
- package/dist/schema-migrations.js.map +1 -0
- package/dist/schema-validator.d.ts +76 -0
- package/dist/schema-validator.d.ts.map +1 -0
- package/dist/schema-validator.js +219 -0
- package/dist/schema-validator.js.map +1 -0
- package/dist/schema-write.d.ts +122 -0
- package/dist/schema-write.d.ts.map +1 -0
- package/dist/schema-write.js +210 -0
- package/dist/schema-write.js.map +1 -0
- package/dist/status-vocab.d.ts +60 -0
- package/dist/status-vocab.d.ts.map +1 -0
- package/dist/status-vocab.js +109 -0
- package/dist/status-vocab.js.map +1 -0
- package/dist/test-helpers.d.ts +12 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +28 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/topo.d.ts +19 -0
- package/dist/topo.d.ts.map +1 -0
- package/dist/topo.js +99 -0
- package/dist/topo.js.map +1 -0
- package/dist/update-check.d.ts +6 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +82 -0
- package/dist/update-check.js.map +1 -0
- package/package.json +115 -0
- package/samples/blocks/context-contracts.json +3 -0
- package/samples/blocks/conventions.json +3 -0
- package/samples/blocks/decisions.json +3 -0
- package/samples/blocks/features.json +3 -0
- package/samples/blocks/framework-gaps.json +3 -0
- package/samples/blocks/issues.json +3 -0
- package/samples/blocks/layer-plans.json +3 -0
- package/samples/blocks/phase.json +3 -0
- package/samples/blocks/rationale.json +3 -0
- package/samples/blocks/requirements.json +3 -0
- package/samples/blocks/research.json +3 -0
- package/samples/blocks/spec-reviews.json +3 -0
- package/samples/blocks/story.json +3 -0
- package/samples/blocks/tasks.json +3 -0
- package/samples/blocks/verification.json +3 -0
- package/samples/conception.json +467 -0
- package/samples/schemas/context-contracts.schema.json +89 -0
- package/samples/schemas/conventions.schema.json +34 -0
- package/samples/schemas/decisions.schema.json +87 -0
- package/samples/schemas/features.schema.json +53 -0
- package/samples/schemas/framework-gaps.schema.json +64 -0
- package/samples/schemas/issues.schema.json +39 -0
- package/samples/schemas/layer-plans.schema.json +92 -0
- package/samples/schemas/phase.schema.json +118 -0
- package/samples/schemas/rationale.schema.json +24 -0
- package/samples/schemas/requirements.schema.json +34 -0
- package/samples/schemas/research.schema.json +134 -0
- package/samples/schemas/spec-reviews.schema.json +62 -0
- package/samples/schemas/story.schema.json +73 -0
- package/samples/schemas/tasks.schema.json +34 -0
- package/samples/schemas/verification.schema.json +37 -0
- package/schemas/bootstrap.schema.json +24 -0
- package/schemas/config.schema.json +193 -0
- package/schemas/layer.schema.json +9 -0
- package/schemas/priority.schema.json +9 -0
- package/schemas/relations.schema.json +31 -0
- package/schemas/severity.schema.json +9 -0
- package/schemas/source.schema.json +9 -0
- package/schemas/status.schema.json +9 -0
- package/schemas/verification-method.schema.json +9 -0
- package/skill-narrative.md +130 -0
- package/skills/pi-context/SKILL.md +684 -0
- package/skills/pi-context/references/bundled-resources.md +47 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Samples-catalog discovery surface (FGAP-068 closure; DEC-0037 packaged view).
|
|
3
|
+
*
|
|
4
|
+
* DEC-0037 (enacted) requires the conception present each block kind PACKAGED
|
|
5
|
+
* with its relation_types + invariants + lens. This module reads the
|
|
6
|
+
* extension's OWN bundled `samples/conception.json` + `samples/schemas/` and
|
|
7
|
+
* projects, per block kind: title, description, item shape, the relation_types
|
|
8
|
+
* for which the kind may be a source / target endpoint, the invariants ranging
|
|
9
|
+
* over it, and the lenses targeting it — plus the top-level relation_type /
|
|
10
|
+
* lens / invariant / layer / status_bucket registries.
|
|
11
|
+
*
|
|
12
|
+
* PACKAGE-INTRINSIC: there is no `cwd` parameter. The catalog reads the bundled
|
|
13
|
+
* samples directory resolved relative to this module (`../samples`), so it works
|
|
14
|
+
* the same from `src/` (tsx --test) and from `dist/` (post-build) and is
|
|
15
|
+
* independent of any installed project substrate.
|
|
16
|
+
*
|
|
17
|
+
* DEC-0019/0020 dual-surface: this library function backs both the Pi tool
|
|
18
|
+
* `read-samples-catalog` (in-pi LLM discovery) and the CLI script
|
|
19
|
+
* `scripts/orchestrator/read-samples-catalog.ts` (Claude-Code-side); all three
|
|
20
|
+
* ship as one unit.
|
|
21
|
+
*
|
|
22
|
+
* Never throws for unknown-kind / missing-description: defects surface as
|
|
23
|
+
* per-kind `warnings[]` and catalog-level `warnings[]` so a half-authored
|
|
24
|
+
* conception is diagnosable rather than fatal.
|
|
25
|
+
*/
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { schemaInfoFromPath } from "./context-sdk.js";
|
|
30
|
+
/** Bundled samples directory — resolved relative to this module so it works from
|
|
31
|
+
* both `src/` (tsx) and `dist/` (built); `samples/` sits one level above either. */
|
|
32
|
+
const SAMPLES_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "samples");
|
|
33
|
+
/**
|
|
34
|
+
* Build the packaged samples catalog. With `opts.kind` set, the catalog's
|
|
35
|
+
* `kinds[]` is filtered to the single matching block kind (empty + a warning
|
|
36
|
+
* when no kind matches). The top-level registries are always returned in full.
|
|
37
|
+
*/
|
|
38
|
+
export function samplesCatalog(opts) {
|
|
39
|
+
const conception = JSON.parse(fs.readFileSync(path.join(SAMPLES_DIR, "conception.json"), "utf-8"));
|
|
40
|
+
const blockKinds = conception.block_kinds ?? [];
|
|
41
|
+
const relationTypes = conception.relation_types ?? [];
|
|
42
|
+
const lenses = conception.lenses ?? [];
|
|
43
|
+
const invariants = conception.invariants ?? [];
|
|
44
|
+
const warnings = [];
|
|
45
|
+
const knownKindIds = new Set(blockKinds.map((bk) => bk.canonical_id));
|
|
46
|
+
// Catalog-level: any source/target_kinds value that is neither "*" nor a
|
|
47
|
+
// known block_kind canonical_id is a misauthored endpoint declaration.
|
|
48
|
+
for (const rt of relationTypes) {
|
|
49
|
+
for (const k of rt.source_kinds ?? []) {
|
|
50
|
+
if (k !== "*" && !knownKindIds.has(k)) {
|
|
51
|
+
warnings.push(`relation_type '${rt.canonical_id}' names unknown kind '${k}' in source_kinds`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const k of rt.target_kinds ?? []) {
|
|
55
|
+
if (k !== "*" && !knownKindIds.has(k)) {
|
|
56
|
+
warnings.push(`relation_type '${rt.canonical_id}' names unknown kind '${k}' in target_kinds`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Filter set: when opts.kind is given but unknown, emit a warning + empty kinds.
|
|
61
|
+
let selected = blockKinds;
|
|
62
|
+
if (opts?.kind !== undefined) {
|
|
63
|
+
selected = blockKinds.filter((bk) => bk.canonical_id === opts.kind);
|
|
64
|
+
if (selected.length === 0) {
|
|
65
|
+
warnings.push(`unknown kind '${opts.kind}' — not in block_kinds`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const kinds = selected.map((bk) => buildSampleKind(bk, relationTypes, invariants, lenses));
|
|
69
|
+
return {
|
|
70
|
+
schema_version: String(conception.schema_version ?? ""),
|
|
71
|
+
kinds,
|
|
72
|
+
relationTypes,
|
|
73
|
+
lenses,
|
|
74
|
+
invariants,
|
|
75
|
+
layers: conception.layers ?? [],
|
|
76
|
+
status_buckets: conception.status_buckets ?? {},
|
|
77
|
+
warnings,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Project one block kind into a SampleKind, reading its bundled schema. */
|
|
81
|
+
function buildSampleKind(bk, relationTypes, invariants, lenses) {
|
|
82
|
+
const kindWarnings = [];
|
|
83
|
+
// Read the schema once for the root description; schemaInfoFromPath handles
|
|
84
|
+
// title + item shape (it does not capture the root `description` field).
|
|
85
|
+
const schemaFile = path.join(SAMPLES_DIR, "schemas", `${bk.canonical_id}.schema.json`);
|
|
86
|
+
let description = null;
|
|
87
|
+
try {
|
|
88
|
+
const rawSchema = JSON.parse(fs.readFileSync(schemaFile, "utf-8"));
|
|
89
|
+
description = typeof rawSchema.description === "string" ? rawSchema.description : null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
description = null;
|
|
93
|
+
}
|
|
94
|
+
const info = schemaInfoFromPath(schemaFile, bk.canonical_id);
|
|
95
|
+
const title = info?.title ?? null;
|
|
96
|
+
const shape = info
|
|
97
|
+
? { arrayKeys: info.arrayKeys, itemProperties: info.itemProperties ?? {} }
|
|
98
|
+
: null;
|
|
99
|
+
if (title === null)
|
|
100
|
+
kindWarnings.push(`kind '${bk.canonical_id}': schema title missing or unreadable`);
|
|
101
|
+
if (description === null)
|
|
102
|
+
kindWarnings.push(`kind '${bk.canonical_id}': schema description missing or unreadable`);
|
|
103
|
+
if (shape === null)
|
|
104
|
+
kindWarnings.push(`kind '${bk.canonical_id}': schema shape missing or unreadable`);
|
|
105
|
+
// Endpoint participation: R is as_source for K iff R.source_kinds includes
|
|
106
|
+
// "*" or K.canonical_id; as_target analogously. A relation with no
|
|
107
|
+
// source_kinds/target_kinds contributes to neither (metadata-less case).
|
|
108
|
+
const as_source = relationTypes.filter((r) => r.source_kinds !== undefined && (r.source_kinds.includes("*") || r.source_kinds.includes(bk.canonical_id)));
|
|
109
|
+
const as_target = relationTypes.filter((r) => r.target_kinds !== undefined && (r.target_kinds.includes("*") || r.target_kinds.includes(bk.canonical_id)));
|
|
110
|
+
const kindInvariants = invariants.filter((inv) => inv.block === bk.canonical_id);
|
|
111
|
+
const kindLenses = lenses.filter((lens) => lens.target === bk.canonical_id || (Array.isArray(lens.targets) && lens.targets.includes(bk.canonical_id)));
|
|
112
|
+
return {
|
|
113
|
+
canonical_id: bk.canonical_id,
|
|
114
|
+
display_name: bk.display_name,
|
|
115
|
+
prefix: bk.prefix,
|
|
116
|
+
array_key: bk.array_key,
|
|
117
|
+
data_path: bk.data_path,
|
|
118
|
+
schema_path: bk.schema_path,
|
|
119
|
+
title,
|
|
120
|
+
description,
|
|
121
|
+
shape,
|
|
122
|
+
relation_types: { as_source, as_target },
|
|
123
|
+
invariants: kindInvariants,
|
|
124
|
+
lenses: kindLenses,
|
|
125
|
+
warnings: kindWarnings,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=samples-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"samples-catalog.js","sourceRoot":"","sources":["../src/samples-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema migration registry — closes FGAP-006 (schema versioning + identity +
|
|
3
|
+
* composition + migration story) at the runtime-migration layer. Pairs with
|
|
4
|
+
* the `$id` + `version` schema-identity surface landed alongside this module
|
|
5
|
+
* in the framework schemas under `packages/pi-context/schemas/`.
|
|
6
|
+
*
|
|
7
|
+
* Aim: when a block file declares `schema_version: "1.0.0"` but the schema
|
|
8
|
+
* itself has advanced to `"2.0.0"`, a registered chain of MigrationFns walks
|
|
9
|
+
* the data forward one step at a time — `1.0.0 → 1.1.0 → 2.0.0` — before AJV
|
|
10
|
+
* validates against the current schema. Per-step migrations are pure data
|
|
11
|
+
* transforms that take and return `unknown`; the registry resolves the chain
|
|
12
|
+
* by looking up each (schemaName, fromVersion) entry and following its
|
|
13
|
+
* `toVersion` forward until it lands on `targetVersion` or fails.
|
|
14
|
+
*
|
|
15
|
+
* Design notes (intentions, not absolutes):
|
|
16
|
+
* - In-memory module via `createRegistry()` returning the typed surface;
|
|
17
|
+
* a singleton is not exported because per-process / per-test isolation
|
|
18
|
+
* simplifies coverage and avoids cross-test contamination.
|
|
19
|
+
* - Linear chain only — no branching / no diamond resolution. If a future
|
|
20
|
+
* schema has multiple `1.x → 2.x` paths the registry as written will
|
|
21
|
+
* follow the first registered edge from each (schemaName, fromVersion);
|
|
22
|
+
* callers should register exactly one outgoing edge per node.
|
|
23
|
+
* - Cycle detection is bounded by tracking visited (schemaName, version)
|
|
24
|
+
* tuples during `resolve`; an unbounded walk would hang on a misregistered
|
|
25
|
+
* `1.0.0 → 1.0.0` self-loop.
|
|
26
|
+
* - `runMigrations` with `currentVersion === targetVersion` is a no-op
|
|
27
|
+
* pass-through — no registry lookup, no copy, no error.
|
|
28
|
+
* - `register` rejects duplicate (schemaName, fromVersion) entries with a
|
|
29
|
+
* descriptive Error so accidental double-registration surfaces at startup
|
|
30
|
+
* rather than as silent override.
|
|
31
|
+
*
|
|
32
|
+
* Out of scope here:
|
|
33
|
+
* - On-disk migration of existing block files. Migration runs at READ time
|
|
34
|
+
* when a future `validateBlockWithMigration` (schema-validator.ts) is
|
|
35
|
+
* called; it does not rewrite the source file.
|
|
36
|
+
* - Cross-schema migrations (e.g. splitting one block into two). Those
|
|
37
|
+
* would compose at a higher layer than this per-schema chain.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* A single forward migration step. Receives the block data as it exists at
|
|
41
|
+
* `fromVersion` and returns the data shaped to match `toVersion`. The function
|
|
42
|
+
* is expected to be pure; it should not perform I/O.
|
|
43
|
+
*/
|
|
44
|
+
export type MigrationFn = (data: unknown) => unknown;
|
|
45
|
+
/**
|
|
46
|
+
* One registered edge in the migration graph. `schemaName` is the canonical
|
|
47
|
+
* schema id (matches the schema's `$id` minus the URN prefix, e.g. `"config"`,
|
|
48
|
+
* `"relations"`, `"priority"`). `fromVersion` and `toVersion` are semver
|
|
49
|
+
* strings matching the schema's `version` field at those points in time.
|
|
50
|
+
*/
|
|
51
|
+
export interface MigrationRegistryEntry {
|
|
52
|
+
schemaName: string;
|
|
53
|
+
fromVersion: string;
|
|
54
|
+
toVersion: string;
|
|
55
|
+
migrate: MigrationFn;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Typed surface returned by `createRegistry()`. `register` adds an edge;
|
|
59
|
+
* `resolve` walks the registered edges to produce an ordered list of
|
|
60
|
+
* MigrationFns from `fromVersion` to `toVersion`. `resolve` throws when no
|
|
61
|
+
* path exists (including when the registry is empty for that schemaName).
|
|
62
|
+
*/
|
|
63
|
+
export interface MigrationRegistry {
|
|
64
|
+
register(entry: MigrationRegistryEntry): void;
|
|
65
|
+
resolve(schemaName: string, fromVersion: string, toVersion: string): MigrationFn[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Construct a fresh, empty migration registry. Each call returns an
|
|
69
|
+
* independent instance — there is no shared global state — so callers needing
|
|
70
|
+
* isolation (notably tests) get it for free.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createRegistry(): MigrationRegistry;
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the migration chain for `schemaName` from `currentVersion` to
|
|
75
|
+
* `targetVersion` and apply each step in order to `data`. Returns the final
|
|
76
|
+
* migrated data. When `currentVersion === targetVersion` the input is
|
|
77
|
+
* returned unchanged (no allocation, no registry lookup).
|
|
78
|
+
*
|
|
79
|
+
* Throws when no migration path exists (`registry.resolve` propagates).
|
|
80
|
+
*/
|
|
81
|
+
export declare function runMigrations(registry: MigrationRegistry, schemaName: string, currentVersion: string, targetVersion: string, data: unknown): unknown;
|
|
82
|
+
//# sourceMappingURL=schema-migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-migrations.d.ts","sourceRoot":"","sources":["../src/schema-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;CACnF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CA0DlD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,iBAAiB,EAC3B,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,OAAO,GACX,OAAO,CAST"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema migration registry — closes FGAP-006 (schema versioning + identity +
|
|
3
|
+
* composition + migration story) at the runtime-migration layer. Pairs with
|
|
4
|
+
* the `$id` + `version` schema-identity surface landed alongside this module
|
|
5
|
+
* in the framework schemas under `packages/pi-context/schemas/`.
|
|
6
|
+
*
|
|
7
|
+
* Aim: when a block file declares `schema_version: "1.0.0"` but the schema
|
|
8
|
+
* itself has advanced to `"2.0.0"`, a registered chain of MigrationFns walks
|
|
9
|
+
* the data forward one step at a time — `1.0.0 → 1.1.0 → 2.0.0` — before AJV
|
|
10
|
+
* validates against the current schema. Per-step migrations are pure data
|
|
11
|
+
* transforms that take and return `unknown`; the registry resolves the chain
|
|
12
|
+
* by looking up each (schemaName, fromVersion) entry and following its
|
|
13
|
+
* `toVersion` forward until it lands on `targetVersion` or fails.
|
|
14
|
+
*
|
|
15
|
+
* Design notes (intentions, not absolutes):
|
|
16
|
+
* - In-memory module via `createRegistry()` returning the typed surface;
|
|
17
|
+
* a singleton is not exported because per-process / per-test isolation
|
|
18
|
+
* simplifies coverage and avoids cross-test contamination.
|
|
19
|
+
* - Linear chain only — no branching / no diamond resolution. If a future
|
|
20
|
+
* schema has multiple `1.x → 2.x` paths the registry as written will
|
|
21
|
+
* follow the first registered edge from each (schemaName, fromVersion);
|
|
22
|
+
* callers should register exactly one outgoing edge per node.
|
|
23
|
+
* - Cycle detection is bounded by tracking visited (schemaName, version)
|
|
24
|
+
* tuples during `resolve`; an unbounded walk would hang on a misregistered
|
|
25
|
+
* `1.0.0 → 1.0.0` self-loop.
|
|
26
|
+
* - `runMigrations` with `currentVersion === targetVersion` is a no-op
|
|
27
|
+
* pass-through — no registry lookup, no copy, no error.
|
|
28
|
+
* - `register` rejects duplicate (schemaName, fromVersion) entries with a
|
|
29
|
+
* descriptive Error so accidental double-registration surfaces at startup
|
|
30
|
+
* rather than as silent override.
|
|
31
|
+
*
|
|
32
|
+
* Out of scope here:
|
|
33
|
+
* - On-disk migration of existing block files. Migration runs at READ time
|
|
34
|
+
* when a future `validateBlockWithMigration` (schema-validator.ts) is
|
|
35
|
+
* called; it does not rewrite the source file.
|
|
36
|
+
* - Cross-schema migrations (e.g. splitting one block into two). Those
|
|
37
|
+
* would compose at a higher layer than this per-schema chain.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Construct a fresh, empty migration registry. Each call returns an
|
|
41
|
+
* independent instance — there is no shared global state — so callers needing
|
|
42
|
+
* isolation (notably tests) get it for free.
|
|
43
|
+
*/
|
|
44
|
+
export function createRegistry() {
|
|
45
|
+
// Outer map: schemaName → inner map keyed by fromVersion. Each inner-map
|
|
46
|
+
// entry holds the single outgoing edge (toVersion + migrate). One outgoing
|
|
47
|
+
// edge per (schemaName, fromVersion) keeps the walk deterministic.
|
|
48
|
+
const edges = new Map();
|
|
49
|
+
function register(entry) {
|
|
50
|
+
let perSchema = edges.get(entry.schemaName);
|
|
51
|
+
if (!perSchema) {
|
|
52
|
+
perSchema = new Map();
|
|
53
|
+
edges.set(entry.schemaName, perSchema);
|
|
54
|
+
}
|
|
55
|
+
if (perSchema.has(entry.fromVersion)) {
|
|
56
|
+
throw new Error(`MigrationRegistry: duplicate edge for schema '${entry.schemaName}' fromVersion '${entry.fromVersion}'`);
|
|
57
|
+
}
|
|
58
|
+
perSchema.set(entry.fromVersion, { toVersion: entry.toVersion, migrate: entry.migrate });
|
|
59
|
+
}
|
|
60
|
+
function resolve(schemaName, fromVersion, toVersion) {
|
|
61
|
+
if (fromVersion === toVersion)
|
|
62
|
+
return [];
|
|
63
|
+
const perSchema = edges.get(schemaName);
|
|
64
|
+
if (!perSchema || perSchema.size === 0) {
|
|
65
|
+
throw new Error(`MigrationRegistry: no migrations registered for schema '${schemaName}' (need ${fromVersion} → ${toVersion})`);
|
|
66
|
+
}
|
|
67
|
+
const chain = [];
|
|
68
|
+
const visited = new Set();
|
|
69
|
+
let cursor = fromVersion;
|
|
70
|
+
// Walk forward step by step. Each step looks up the outgoing edge from
|
|
71
|
+
// `cursor`; if there is none, the requested `toVersion` is unreachable.
|
|
72
|
+
while (cursor !== toVersion) {
|
|
73
|
+
if (visited.has(cursor)) {
|
|
74
|
+
throw new Error(`MigrationRegistry: cycle detected at schema '${schemaName}' version '${cursor}' while resolving ${fromVersion} → ${toVersion}`);
|
|
75
|
+
}
|
|
76
|
+
visited.add(cursor);
|
|
77
|
+
const next = perSchema.get(cursor);
|
|
78
|
+
if (!next) {
|
|
79
|
+
throw new Error(`MigrationRegistry: no path from ${fromVersion} to ${toVersion} for schema '${schemaName}' (stuck at '${cursor}')`);
|
|
80
|
+
}
|
|
81
|
+
chain.push(next.migrate);
|
|
82
|
+
cursor = next.toVersion;
|
|
83
|
+
}
|
|
84
|
+
return chain;
|
|
85
|
+
}
|
|
86
|
+
return { register, resolve };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the migration chain for `schemaName` from `currentVersion` to
|
|
90
|
+
* `targetVersion` and apply each step in order to `data`. Returns the final
|
|
91
|
+
* migrated data. When `currentVersion === targetVersion` the input is
|
|
92
|
+
* returned unchanged (no allocation, no registry lookup).
|
|
93
|
+
*
|
|
94
|
+
* Throws when no migration path exists (`registry.resolve` propagates).
|
|
95
|
+
*/
|
|
96
|
+
export function runMigrations(registry, schemaName, currentVersion, targetVersion, data) {
|
|
97
|
+
if (currentVersion === targetVersion)
|
|
98
|
+
return data;
|
|
99
|
+
const chain = registry.resolve(schemaName, currentVersion, targetVersion);
|
|
100
|
+
let cursor = data;
|
|
101
|
+
for (const step of chain) {
|
|
102
|
+
cursor = step(cursor);
|
|
103
|
+
}
|
|
104
|
+
return cursor;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=schema-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-migrations.js","sourceRoot":"","sources":["../src/schema-migrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ErrorObject } from "ajv";
|
|
2
|
+
import { type MigrationRegistry } from "./schema-migrations.js";
|
|
3
|
+
/**
|
|
4
|
+
* Error class for validation failures.
|
|
5
|
+
* Contains the original AJV errors and a formatted message.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ValidationError extends Error {
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly errors: ErrorObject[];
|
|
10
|
+
constructor(label: string, errors: ErrorObject[]);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validate data against a JSON Schema object.
|
|
14
|
+
* Throws ValidationError with formatted error messages on failure.
|
|
15
|
+
* Returns the validated data on success (pass-through).
|
|
16
|
+
*
|
|
17
|
+
* `$ref` to any pre-registered framework schema (URN form
|
|
18
|
+
* `pi-context://schemas/<name>`) resolves synchronously because those schemas
|
|
19
|
+
* were added to the shared AJV instance at module init.
|
|
20
|
+
*
|
|
21
|
+
* When the supplied schema carries an `$id` that already matches a registered
|
|
22
|
+
* schema on this AJV instance (e.g. the framework `config` / `relations`
|
|
23
|
+
* schemas pre-registered above), the cached compiled validator is reused
|
|
24
|
+
* rather than re-adding the schema — re-adding a duplicate `$id` throws
|
|
25
|
+
* "schema with key or id ... already exists".
|
|
26
|
+
*/
|
|
27
|
+
export declare function validate(schema: Record<string, unknown>, data: unknown, label: string): unknown;
|
|
28
|
+
/**
|
|
29
|
+
* Validate that `schema` itself is a structurally-valid JSON Schema.
|
|
30
|
+
* Routes through AJV's bundled draft-07 meta-schema via `ajv.validateSchema`,
|
|
31
|
+
* which is the canonical "is this a schema?" check on the same AJV instance
|
|
32
|
+
* already in use for data validation. Throws `ValidationError` listing every
|
|
33
|
+
* meta-schema violation when the schema is malformed (e.g. unknown keyword
|
|
34
|
+
* combinations, invalid `type` value, malformed `properties`). Returns the
|
|
35
|
+
* input on success (pass-through, mirrors `validate`).
|
|
36
|
+
*
|
|
37
|
+
* Reusing the module-internal `ajv` instance is intentional per the rebuild
|
|
38
|
+
* arc — the schema-write surface (FGAP-011) must not stand up a parallel AJV
|
|
39
|
+
* instance with diverging strictness / format settings.
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateSchemaAgainstMeta(schema: unknown, label: string): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Load a JSON Schema from a file path and validate data against it.
|
|
44
|
+
* Throws if the schema file doesn't exist or is invalid JSON.
|
|
45
|
+
* Throws ValidationError on validation failure.
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateFromFile(schemaPath: string, data: unknown, label: string): unknown;
|
|
48
|
+
/**
|
|
49
|
+
* Migration-aware block validation (FGAP-006 read-time migration surface).
|
|
50
|
+
*
|
|
51
|
+
* Aim: read a block file from disk, compare its declared `schema_version`
|
|
52
|
+
* against the framework schema's `version`, run any registered migrations
|
|
53
|
+
* forward via `runMigrations`, then validate the migrated data against the
|
|
54
|
+
* current schema. This is the single entry point that lets a project on an
|
|
55
|
+
* older block format keep loading after a schema bump, without rewriting the
|
|
56
|
+
* file on disk.
|
|
57
|
+
*
|
|
58
|
+
* Behaviour:
|
|
59
|
+
* - `cwd` resolves to `<contextDir>/schemas/<schemaName>.schema.json` for
|
|
60
|
+
* the schema; user-state schemas under `.project/schemas/` win over
|
|
61
|
+
* framework schemas because that path is computed relative to `cwd`.
|
|
62
|
+
* - `data.schema_version` is read off the supplied data when present. When
|
|
63
|
+
* it is missing, the data is validated as-is (no migration attempted) —
|
|
64
|
+
* pre-FGAP-006 blocks have no version field and should pass through.
|
|
65
|
+
* - `registry` is optional; when omitted and the versions differ, the
|
|
66
|
+
* function throws (no chain to apply).
|
|
67
|
+
* - On match (`block.schema_version === schema.version`) or missing
|
|
68
|
+
* `schema_version` field, no migration runs and the data goes straight
|
|
69
|
+
* to `validate()`.
|
|
70
|
+
* - The migrated data is what gets validated; the input is not mutated.
|
|
71
|
+
*
|
|
72
|
+
* Out of scope: writing the migrated form back to disk — call sites that need
|
|
73
|
+
* persistence handle that themselves via the block-write surface.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateBlockWithMigration(cwd: string, schemaName: string, data: unknown, registry?: MigrationRegistry): unknown;
|
|
76
|
+
//# sourceMappingURL=schema-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAIvC,OAAO,EAAE,KAAK,iBAAiB,EAAiB,MAAM,wBAAwB,CAAC;AAsE/E;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;gBAEnB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;CAOhD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAqB/F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAgB1F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,0BAA0B,CACzC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,QAAQ,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAgCT"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import _Ajv from "ajv";
|
|
5
|
+
import _addFormats from "ajv-formats";
|
|
6
|
+
import { schemaPath as schemaPathHelper } from "./context-dir.js";
|
|
7
|
+
import { runMigrations } from "./schema-migrations.js";
|
|
8
|
+
// Node16 module resolution + CJS interop: default import is the module namespace
|
|
9
|
+
const Ajv = _Ajv.default ?? _Ajv;
|
|
10
|
+
const addFormats = _addFormats.default ?? _addFormats;
|
|
11
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
12
|
+
addFormats(ajv);
|
|
13
|
+
/**
|
|
14
|
+
* Pre-register the eight framework schemas (`config`, `relations`, plus the
|
|
15
|
+
* six FGAP-016 shared enums: priority, status, severity, source, layer,
|
|
16
|
+
* verification-method) at AJV-instance construction so cross-schema `$ref`
|
|
17
|
+
* to `pi-context://schemas/<name>` resolves synchronously without an async
|
|
18
|
+
* `loadSchema` hook. Pre-registration also makes the URN identity surface
|
|
19
|
+
* (FGAP-006 closure) usable from any caller of this module without each
|
|
20
|
+
* caller restating the schemas.
|
|
21
|
+
*
|
|
22
|
+
* Pre-registration is best-effort: if a schema file is missing or malformed
|
|
23
|
+
* (which would only happen during local dev with a half-applied edit), the
|
|
24
|
+
* load throws synchronously at module init — the failure surfaces immediately
|
|
25
|
+
* rather than as a confusing late `$ref` resolution error.
|
|
26
|
+
*
|
|
27
|
+
* The schema directory is resolved relative to this file, which means the
|
|
28
|
+
* pre-registration works the same way whether the package is consumed from
|
|
29
|
+
* source (via tsx) or from `dist/` post-build, because the schemas/ directory
|
|
30
|
+
* sits one level above either `src/` or `dist/`.
|
|
31
|
+
*/
|
|
32
|
+
const FRAMEWORK_SCHEMA_NAMES = [
|
|
33
|
+
"config",
|
|
34
|
+
"relations",
|
|
35
|
+
"bootstrap",
|
|
36
|
+
"priority",
|
|
37
|
+
"status",
|
|
38
|
+
"severity",
|
|
39
|
+
"source",
|
|
40
|
+
"layer",
|
|
41
|
+
"verification-method",
|
|
42
|
+
];
|
|
43
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
44
|
+
// `__dirname` points at either `<pkg>/src/` (tsx) or `<pkg>/dist/` (built).
|
|
45
|
+
// Either way, `../schemas/` lands at `<pkg>/schemas/` because both `src/` and
|
|
46
|
+
// `dist/` are direct children of the package root.
|
|
47
|
+
const SCHEMAS_ROOT = path.resolve(__dirname, "..", "schemas");
|
|
48
|
+
for (const name of FRAMEWORK_SCHEMA_NAMES) {
|
|
49
|
+
const schemaPath = path.join(SCHEMAS_ROOT, `${name}.schema.json`);
|
|
50
|
+
if (!fs.existsSync(schemaPath)) {
|
|
51
|
+
throw new Error(`schema-validator: framework schema missing at module init: ${schemaPath}`);
|
|
52
|
+
}
|
|
53
|
+
const raw = fs.readFileSync(schemaPath, "utf-8");
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
throw new Error(`schema-validator: failed to parse framework schema ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
60
|
+
}
|
|
61
|
+
// addSchema is idempotent when the same key is registered twice with
|
|
62
|
+
// identical content, but we guard anyway so reloads in test contexts
|
|
63
|
+
// (which they are not currently doing, but might in future) do not throw
|
|
64
|
+
// on duplicate-id errors.
|
|
65
|
+
const id = parsed.$id;
|
|
66
|
+
if (id && ajv.getSchema(id)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
ajv.addSchema(parsed, id);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Error class for validation failures.
|
|
73
|
+
* Contains the original AJV errors and a formatted message.
|
|
74
|
+
*/
|
|
75
|
+
export class ValidationError extends Error {
|
|
76
|
+
label;
|
|
77
|
+
errors;
|
|
78
|
+
constructor(label, errors) {
|
|
79
|
+
const details = errors.map((e) => `${e.instancePath || ""}: ${e.message}`).join("; ");
|
|
80
|
+
super(`Validation failed for ${label}: ${details}`);
|
|
81
|
+
this.name = "ValidationError";
|
|
82
|
+
this.label = label;
|
|
83
|
+
this.errors = errors;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate data against a JSON Schema object.
|
|
88
|
+
* Throws ValidationError with formatted error messages on failure.
|
|
89
|
+
* Returns the validated data on success (pass-through).
|
|
90
|
+
*
|
|
91
|
+
* `$ref` to any pre-registered framework schema (URN form
|
|
92
|
+
* `pi-context://schemas/<name>`) resolves synchronously because those schemas
|
|
93
|
+
* were added to the shared AJV instance at module init.
|
|
94
|
+
*
|
|
95
|
+
* When the supplied schema carries an `$id` that already matches a registered
|
|
96
|
+
* schema on this AJV instance (e.g. the framework `config` / `relations`
|
|
97
|
+
* schemas pre-registered above), the cached compiled validator is reused
|
|
98
|
+
* rather than re-adding the schema — re-adding a duplicate `$id` throws
|
|
99
|
+
* "schema with key or id ... already exists".
|
|
100
|
+
*/
|
|
101
|
+
export function validate(schema, data, label) {
|
|
102
|
+
const id = typeof schema.$id === "string" ? schema.$id : undefined;
|
|
103
|
+
let valid;
|
|
104
|
+
let errors;
|
|
105
|
+
if (id) {
|
|
106
|
+
const cached = ajv.getSchema(id);
|
|
107
|
+
if (cached) {
|
|
108
|
+
valid = cached(data);
|
|
109
|
+
errors = cached.errors;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
valid = ajv.validate(schema, data);
|
|
113
|
+
errors = ajv.errors;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
valid = ajv.validate(schema, data);
|
|
118
|
+
errors = ajv.errors;
|
|
119
|
+
}
|
|
120
|
+
if (!valid) {
|
|
121
|
+
throw new ValidationError(label, errors ?? []);
|
|
122
|
+
}
|
|
123
|
+
return data;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Validate that `schema` itself is a structurally-valid JSON Schema.
|
|
127
|
+
* Routes through AJV's bundled draft-07 meta-schema via `ajv.validateSchema`,
|
|
128
|
+
* which is the canonical "is this a schema?" check on the same AJV instance
|
|
129
|
+
* already in use for data validation. Throws `ValidationError` listing every
|
|
130
|
+
* meta-schema violation when the schema is malformed (e.g. unknown keyword
|
|
131
|
+
* combinations, invalid `type` value, malformed `properties`). Returns the
|
|
132
|
+
* input on success (pass-through, mirrors `validate`).
|
|
133
|
+
*
|
|
134
|
+
* Reusing the module-internal `ajv` instance is intentional per the rebuild
|
|
135
|
+
* arc — the schema-write surface (FGAP-011) must not stand up a parallel AJV
|
|
136
|
+
* instance with diverging strictness / format settings.
|
|
137
|
+
*/
|
|
138
|
+
export function validateSchemaAgainstMeta(schema, label) {
|
|
139
|
+
const valid = ajv.validateSchema(schema);
|
|
140
|
+
if (valid !== true) {
|
|
141
|
+
throw new ValidationError(label, ajv.errors ?? []);
|
|
142
|
+
}
|
|
143
|
+
return schema;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Load a JSON Schema from a file path and validate data against it.
|
|
147
|
+
* Throws if the schema file doesn't exist or is invalid JSON.
|
|
148
|
+
* Throws ValidationError on validation failure.
|
|
149
|
+
*/
|
|
150
|
+
export function validateFromFile(schemaPath, data, label) {
|
|
151
|
+
let content;
|
|
152
|
+
try {
|
|
153
|
+
content = fs.readFileSync(schemaPath, "utf-8");
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
throw new Error(`Schema file not found: ${schemaPath}`);
|
|
157
|
+
}
|
|
158
|
+
let schema;
|
|
159
|
+
try {
|
|
160
|
+
schema = JSON.parse(content);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
throw new Error(`Invalid JSON in schema file: ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
164
|
+
}
|
|
165
|
+
return validate(schema, data, label);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Migration-aware block validation (FGAP-006 read-time migration surface).
|
|
169
|
+
*
|
|
170
|
+
* Aim: read a block file from disk, compare its declared `schema_version`
|
|
171
|
+
* against the framework schema's `version`, run any registered migrations
|
|
172
|
+
* forward via `runMigrations`, then validate the migrated data against the
|
|
173
|
+
* current schema. This is the single entry point that lets a project on an
|
|
174
|
+
* older block format keep loading after a schema bump, without rewriting the
|
|
175
|
+
* file on disk.
|
|
176
|
+
*
|
|
177
|
+
* Behaviour:
|
|
178
|
+
* - `cwd` resolves to `<contextDir>/schemas/<schemaName>.schema.json` for
|
|
179
|
+
* the schema; user-state schemas under `.project/schemas/` win over
|
|
180
|
+
* framework schemas because that path is computed relative to `cwd`.
|
|
181
|
+
* - `data.schema_version` is read off the supplied data when present. When
|
|
182
|
+
* it is missing, the data is validated as-is (no migration attempted) —
|
|
183
|
+
* pre-FGAP-006 blocks have no version field and should pass through.
|
|
184
|
+
* - `registry` is optional; when omitted and the versions differ, the
|
|
185
|
+
* function throws (no chain to apply).
|
|
186
|
+
* - On match (`block.schema_version === schema.version`) or missing
|
|
187
|
+
* `schema_version` field, no migration runs and the data goes straight
|
|
188
|
+
* to `validate()`.
|
|
189
|
+
* - The migrated data is what gets validated; the input is not mutated.
|
|
190
|
+
*
|
|
191
|
+
* Out of scope: writing the migrated form back to disk — call sites that need
|
|
192
|
+
* persistence handle that themselves via the block-write surface.
|
|
193
|
+
*/
|
|
194
|
+
export function validateBlockWithMigration(cwd, schemaName, data, registry) {
|
|
195
|
+
const schemaPath = schemaPathHelper(cwd, schemaName);
|
|
196
|
+
if (!fs.existsSync(schemaPath)) {
|
|
197
|
+
throw new Error(`validateBlockWithMigration: schema file not found at ${schemaPath}`);
|
|
198
|
+
}
|
|
199
|
+
let schema;
|
|
200
|
+
try {
|
|
201
|
+
schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
throw new Error(`validateBlockWithMigration: invalid JSON in ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
205
|
+
}
|
|
206
|
+
const schemaVersion = typeof schema.version === "string" ? schema.version : undefined;
|
|
207
|
+
const blockVersion = data && typeof data === "object" && "schema_version" in data
|
|
208
|
+
? data.schema_version
|
|
209
|
+
: undefined;
|
|
210
|
+
let toValidate = data;
|
|
211
|
+
if (schemaVersion && blockVersion && schemaVersion !== blockVersion) {
|
|
212
|
+
if (!registry) {
|
|
213
|
+
throw new Error(`validateBlockWithMigration: block at ${schemaName} declares schema_version '${blockVersion}' but schema is at '${schemaVersion}' and no MigrationRegistry was supplied`);
|
|
214
|
+
}
|
|
215
|
+
toValidate = runMigrations(registry, schemaName, blockVersion, schemaVersion, data);
|
|
216
|
+
}
|
|
217
|
+
return validate(schema, toValidate, schemaName);
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=schema-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"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"}
|