@kubohiroya/turbowarp-extension-data-editor 0.1.0 → 0.2.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.
Files changed (91) hide show
  1. package/README.md +24 -3
  2. package/dist/build-info.json +7 -2
  3. package/dist/descendants.js +11 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/json-value.d.ts +4 -0
  6. package/dist/json-value.js +12 -0
  7. package/dist/prepared-save.d.ts +5 -0
  8. package/dist/prepared-save.js +47 -0
  9. package/dist/prepared-types.d.ts +13 -0
  10. package/dist/prepared-types.js +1 -0
  11. package/dist/registry.d.ts +2 -0
  12. package/dist/registry.js +57 -45
  13. package/dist/rjsf.d.ts +25 -1
  14. package/dist/rjsf.js +70 -6
  15. package/dist/schema-compose.d.ts +6 -0
  16. package/dist/schema-compose.js +103 -0
  17. package/dist/schema-engine.d.ts +18 -0
  18. package/dist/schema-engine.js +298 -0
  19. package/dist/schema-json.d.ts +10 -0
  20. package/dist/schema-json.js +68 -0
  21. package/dist/schema-types.d.ts +161 -0
  22. package/dist/schema-types.js +2 -0
  23. package/dist/schema-view.d.ts +7 -0
  24. package/dist/schema-view.js +102 -0
  25. package/dist/schema.d.ts +8 -0
  26. package/dist/schema.js +86 -0
  27. package/dist/session.d.ts +1 -1
  28. package/dist/session.js +18 -5
  29. package/dist/source-document.d.ts +3 -0
  30. package/dist/source-document.js +415 -0
  31. package/dist/source-editor.d.ts +13 -0
  32. package/dist/source-editor.js +235 -0
  33. package/dist/source-schema-hints.d.ts +3 -0
  34. package/dist/source-schema-hints.js +13 -0
  35. package/dist/source-types.d.ts +47 -0
  36. package/dist/source-types.js +1 -0
  37. package/dist/source-view.d.ts +3 -0
  38. package/dist/source-view.js +318 -0
  39. package/dist/types.d.ts +2 -4
  40. package/dist/webpack4/index.js +4 -2
  41. package/dist/webpack4/metafile.json +9551 -8644
  42. package/dist/webpack4/react.js +2 -1
  43. package/dist/webpack4/react.js.map +1 -1
  44. package/dist/webpack4/rjsf.js +15014 -20975
  45. package/dist/webpack4/rjsf.js.map +4 -4
  46. package/dist/webpack4/schema.js +17 -0
  47. package/dist/webpack4/schema.js.map +7 -0
  48. package/dist/webpack4/shared-BKTRZUZH.js +42 -0
  49. package/dist/webpack4/{shared-KIFRVTSM.js.map → shared-BKTRZUZH.js.map} +1 -1
  50. package/dist/webpack4/shared-KQOUMWJ4.js +8490 -0
  51. package/dist/webpack4/shared-KQOUMWJ4.js.map +7 -0
  52. package/dist/webpack4/shared-MRPGBJIH.js +75 -0
  53. package/dist/webpack4/shared-MRPGBJIH.js.map +7 -0
  54. package/dist/webpack4/{shared-WHOAW2VF.js → shared-N6YQE2X6.js} +116 -44
  55. package/dist/webpack4/shared-N6YQE2X6.js.map +7 -0
  56. package/dist/webpack4/{shared-KIFRVTSM.js → shared-RN7GOT65.js} +2 -41
  57. package/dist/webpack4/shared-RN7GOT65.js.map +7 -0
  58. package/dist/webpack4/turbowarp.js +4 -2
  59. package/dist/webpack4/turbowarp.js.map +1 -1
  60. package/docs/README.md +1 -0
  61. package/docs/block-editor-e2e.md +98 -16
  62. package/docs/ci-success-gate.md +84 -0
  63. package/docs/editor/issue-12-public-validation/README.md +61 -0
  64. package/docs/editor/issue-12-public-validation/dark-invalid.png +0 -0
  65. package/docs/editor/issue-12-public-validation/dark-styles.json +173 -0
  66. package/docs/editor/issue-12-public-validation/dark-valid.png +0 -0
  67. package/docs/editor/issue-12-public-validation/deployment-target.json +5 -0
  68. package/docs/editor/issue-12-public-validation/deployment-wait.json +7 -0
  69. package/docs/editor/issue-12-public-validation/light-invalid.png +0 -0
  70. package/docs/editor/issue-12-public-validation/light-styles.json +173 -0
  71. package/docs/editor/issue-12-public-validation/light-valid.png +0 -0
  72. package/docs/editor/issue-12-public-validation/provenance.json +88 -0
  73. package/docs/editor/issue-12-public-validation/results.json +677 -0
  74. package/docs/editor/issue-12-public-validation/summary.json +206 -0
  75. package/docs/github-pages.md +48 -41
  76. package/docs/package-distribution.md +2 -2
  77. package/docs/schema-stage1-browser.json +10 -0
  78. package/docs/schema-stage1-contracts.json +242 -0
  79. package/docs/schema-stage1-dependencies.json +202 -0
  80. package/docs/schema-stage1-git-artifact-20261018.ja.md +17 -0
  81. package/docs/schema-stage1-profile-compat.json +30 -0
  82. package/docs/schema-stage1-review-fixes-20261018.ja.md +36 -0
  83. package/docs/schema-stage1-verification.json +541 -0
  84. package/docs/schema-stage1.ja.md +80 -0
  85. package/docs/schema.md +43 -0
  86. package/docs/source-forms.ja.md +19 -0
  87. package/docs/stage-2-publication.ja.md +29 -0
  88. package/docs/template-fixture-separation.ja.md +43 -0
  89. package/package.json +11 -3
  90. package/webpack4.cjs +1 -1
  91. package/dist/webpack4/shared-WHOAW2VF.js.map +0 -7
@@ -0,0 +1,103 @@
1
+ import { scopeFor } from './schema-engine.js';
2
+ import { isJsonRecord, JsonSnapshotError, snapshotJson } from './schema-json.js';
3
+ function deepFreeze(value) {
4
+ if (value !== null && typeof value === 'object') {
5
+ Object.values(value).forEach(deepFreeze);
6
+ Object.freeze(value);
7
+ }
8
+ return value;
9
+ }
10
+ function ownerFailure(scope, code) {
11
+ return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([
12
+ Object.freeze({ scope, phase: 'execution', operation: 'semantic', code })
13
+ ]) });
14
+ }
15
+ function asyncResult(value) {
16
+ // Handle rejected native/cross-realm promises without running a user-defined `then` getter.
17
+ try {
18
+ void Promise.prototype.then.call(value, () => undefined, () => undefined);
19
+ return true;
20
+ }
21
+ catch ( /* Non-promises have no Promise internal slots. */_a) { /* Non-promises have no Promise internal slots. */ }
22
+ return 'then' in value;
23
+ }
24
+ /** Validate one protected candidate/context snapshot, then invoke a synchronous owner at most once. */
25
+ export function validateSchemaInput(compiled, data, options) {
26
+ let scope = scopeFor(compiled);
27
+ let context;
28
+ let owner;
29
+ try {
30
+ if (!isJsonRecord(options))
31
+ return ownerFailure(scope, 'owner-context-invalid');
32
+ const descriptor = Object.getOwnPropertyDescriptor(options, 'context');
33
+ if (!descriptor || !('value' in descriptor))
34
+ return ownerFailure(scope, 'owner-context-invalid');
35
+ const candidate = snapshotJson(descriptor.value);
36
+ if (!isJsonRecord(candidate) || typeof candidate.consumerId !== 'string' ||
37
+ typeof candidate.nowMs !== 'number' || !Number.isFinite(candidate.nowMs) || !Object.hasOwn(candidate, 'settings')) {
38
+ return ownerFailure(scope, 'owner-context-invalid');
39
+ }
40
+ context = deepFreeze(candidate);
41
+ scope = Object.freeze({ ...scope, consumerId: context.consumerId });
42
+ const ownerDescriptor = Object.getOwnPropertyDescriptor(options, 'ownerValidate');
43
+ if (ownerDescriptor && !('value' in ownerDescriptor))
44
+ return ownerFailure(scope, 'owner-result-invalid');
45
+ owner = ownerDescriptor === null || ownerDescriptor === void 0 ? void 0 : ownerDescriptor.value;
46
+ if (owner !== undefined && typeof owner !== 'function')
47
+ return ownerFailure(scope, 'owner-result-invalid');
48
+ }
49
+ catch (_a) {
50
+ return ownerFailure(scope, 'owner-context-invalid');
51
+ }
52
+ let input;
53
+ try {
54
+ input = deepFreeze(snapshotJson(data));
55
+ const structural = compiled.validate(input, { consumerId: context.consumerId });
56
+ if (!isJsonRecord(structural) || !Array.isArray(structural.diagnostics) ||
57
+ (structural.kind === 'valid' ? structural.diagnostics.length !== 0 : structural.diagnostics.length === 0) ||
58
+ !['valid', 'invalid', 'execution-error'].includes(structural.kind))
59
+ throw new Error();
60
+ if (structural.kind !== 'valid')
61
+ return structural;
62
+ }
63
+ catch (error) {
64
+ if (error instanceof JsonSnapshotError && error.kind === 'invalid')
65
+ return Object.freeze({ kind: 'invalid', diagnostics: Object.freeze([
66
+ Object.freeze({ scope, phase: 'structure', code: 'input-not-json', dataPath: error.dataPath })
67
+ ]) });
68
+ return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([Object.freeze({ scope, phase: 'execution', operation: 'structure',
69
+ code: error instanceof JsonSnapshotError && error.kind === 'limit' || error instanceof RangeError ? 'limit-exceeded' : 'engine-failed' })]) });
70
+ }
71
+ if (!owner)
72
+ return Object.freeze({ kind: 'valid', diagnostics: Object.freeze([]) });
73
+ let result;
74
+ try {
75
+ result = owner(input, context);
76
+ }
77
+ catch (_b) {
78
+ return ownerFailure(scope, 'owner-validation-failed');
79
+ }
80
+ try {
81
+ if (result !== null && (typeof result === 'object' || typeof result === 'function') && asyncResult(result))
82
+ return ownerFailure(scope, 'async-owner-unsupported');
83
+ const snapshot = snapshotJson(result);
84
+ if (!isJsonRecord(snapshot) || !Array.isArray(snapshot.issues))
85
+ return ownerFailure(scope, 'owner-result-invalid');
86
+ if (snapshot.kind === 'valid' && snapshot.issues.length === 0)
87
+ return Object.freeze({ kind: 'valid', diagnostics: Object.freeze([]) });
88
+ if (snapshot.kind !== 'invalid' || snapshot.issues.length === 0)
89
+ return ownerFailure(scope, 'owner-result-invalid');
90
+ const diagnostics = [];
91
+ for (const issue of snapshot.issues) {
92
+ if (!isJsonRecord(issue) || typeof issue.dataPath !== 'string' ||
93
+ (issue.dataPath !== '' && !issue.dataPath.startsWith('/')) || /~(?:[^01]|$)/u.test(issue.dataPath) || typeof issue.ruleId !== 'string') {
94
+ return ownerFailure(scope, 'owner-result-invalid');
95
+ }
96
+ diagnostics.push(Object.freeze({ scope, phase: 'semantic', code: 'owner-input-invalid', dataPath: issue.dataPath, ruleId: issue.ruleId }));
97
+ }
98
+ return Object.freeze({ kind: 'invalid', diagnostics: Object.freeze(diagnostics) });
99
+ }
100
+ catch (_c) {
101
+ return ownerFailure(scope, 'owner-result-invalid');
102
+ }
103
+ }
@@ -0,0 +1,18 @@
1
+ import { Ajv } from 'ajv';
2
+ import { Ajv2020 } from 'ajv/dist/2020.js';
3
+ import type { SchemaBundle, SchemaCompileResult, SchemaDiagnosticScope, SchemaDocument } from './schema-types.js';
4
+ export declare function scopeFor(value: unknown): SchemaDiagnosticScope;
5
+ export declare function prepare(bundle: SchemaBundle, ajv: Ajv | Ajv2020): {
6
+ root: string;
7
+ resources: {
8
+ uri: string;
9
+ document: SchemaDocument;
10
+ }[];
11
+ registry: Map<string, string>;
12
+ nodes: {
13
+ node: Record<string, unknown>;
14
+ base: string;
15
+ }[];
16
+ };
17
+ /** Compile an isolated synchronous validator from an immutable private snapshot. */
18
+ export declare function compileSchemaBundle(input: SchemaBundle): SchemaCompileResult;
@@ -0,0 +1,298 @@
1
+ import { Ajv, MissingRefError } from 'ajv';
2
+ import { Ajv2020 } from 'ajv/dist/2020.js';
3
+ import formats from 'ajv-formats';
4
+ import uri from 'fast-uri';
5
+ import { appendPointer, isJsonRecord, JsonSnapshotError, snapshotJson } from './schema-json.js';
6
+ const DIALECT_07 = 'http://json-schema.org/draft-07/schema#';
7
+ const DIALECT_2020 = 'https://json-schema.org/draft/2020-12/schema';
8
+ const { parse, resolve, normalize } = uri;
9
+ const maps = ['$defs', 'definitions', 'properties', 'patternProperties', 'dependentSchemas', 'dependencies'];
10
+ const singles = ['additionalProperties', 'additionalItems', 'contains', 'propertyNames', 'not', 'if', 'then', 'else',
11
+ 'unevaluatedProperties', 'unevaluatedItems', 'contentSchema'];
12
+ const arrays = ['allOf', 'anyOf', 'oneOf', 'prefixItems'];
13
+ class BundleError extends Error {
14
+ constructor(code) {
15
+ super('Invalid schema bundle.');
16
+ this.code = code;
17
+ }
18
+ }
19
+ function fail(code) { throw new BundleError(code); }
20
+ const ownString = (value, name) => {
21
+ const descriptor = Object.getOwnPropertyDescriptor(value, name);
22
+ return descriptor && 'value' in descriptor && typeof descriptor.value === 'string' ? descriptor.value : undefined;
23
+ };
24
+ const freezeIdentity = (identity) => Object.freeze({ ...identity });
25
+ export function scopeFor(value) {
26
+ const fallback = freezeIdentity({ ownerId: 'unknown', contractId: 'unknown', revision: 'unknown', variantId: 'default' });
27
+ try {
28
+ if (!isJsonRecord(value))
29
+ return Object.freeze({ identity: fallback });
30
+ const descriptor = Object.getOwnPropertyDescriptor(value, 'identity');
31
+ const id = descriptor && 'value' in descriptor ? descriptor.value : null;
32
+ if (!isJsonRecord(id))
33
+ return Object.freeze({ identity: fallback });
34
+ const ownerId = ownString(id, 'ownerId'), contractId = ownString(id, 'contractId');
35
+ const revision = ownString(id, 'revision'), variantId = ownString(id, 'variantId');
36
+ if (ownerId === undefined || contractId === undefined || revision === undefined || variantId === undefined)
37
+ return Object.freeze({ identity: fallback });
38
+ return Object.freeze({ identity: freezeIdentity({ ownerId, contractId, revision, variantId }) });
39
+ }
40
+ catch (_a) {
41
+ return Object.freeze({ identity: fallback });
42
+ }
43
+ }
44
+ function dialectFor(uri) {
45
+ if (typeof uri !== 'string')
46
+ return undefined;
47
+ const canonical = uri.replace(/#$/, '').replace(/^https:/, 'http:');
48
+ if (canonical === 'http://json-schema.org/draft-07/schema')
49
+ return 'draft-07';
50
+ if (canonical === 'http://json-schema.org/draft/2020-12/schema')
51
+ return '2020-12';
52
+ return undefined;
53
+ }
54
+ const normalizeUri = (uri) => normalize(uri).replace(/#$/, '');
55
+ function absoluteUri(uri, fragments) {
56
+ if (typeof uri !== 'string' || !uri)
57
+ return fail('resource-invalid');
58
+ const parsed = parse(uri);
59
+ if (parsed.error || !parsed.scheme || (!fragments && parsed.fragment !== undefined))
60
+ return fail('resource-invalid');
61
+ return normalizeUri(uri);
62
+ }
63
+ function referenceKey(value) {
64
+ var _a;
65
+ if (!isJsonRecord(value) || typeof value.repository !== 'string' || typeof value.path !== 'string')
66
+ return fail('artifact-role-invalid');
67
+ for (const key of ['symbol', 'version', 'sha256'])
68
+ if (key in value && typeof value[key] !== 'string')
69
+ return fail('artifact-role-invalid');
70
+ return JSON.stringify([value.repository, value.path, (_a = value.symbol) !== null && _a !== void 0 ? _a : null]);
71
+ }
72
+ /** Check declared provenance only; paths and retrieval URIs are never fetched. */
73
+ function validateArtifacts(bundle) {
74
+ const provenance = bundle.provenance;
75
+ if (!isJsonRecord(provenance))
76
+ fail('artifact-role-invalid');
77
+ const roles = new Map();
78
+ for (const role of ['canonicalSources', 'generatedFiles', 'referenceSchemas', 'generationSources']) {
79
+ const refs = provenance[role];
80
+ if (!Array.isArray(refs) || (role === 'canonicalSources' && refs.length === 0))
81
+ fail('artifact-role-invalid');
82
+ for (const ref of refs) {
83
+ const key = referenceKey(ref);
84
+ if (roles.has(key))
85
+ fail('artifact-role-invalid');
86
+ roles.set(key, role);
87
+ }
88
+ }
89
+ for (const resource of [bundle.root, ...bundle.resources]) {
90
+ if (!isJsonRecord(resource) || !isJsonRecord(resource.artifact))
91
+ fail('artifact-role-invalid');
92
+ const artifact = resource.artifact;
93
+ const key = referenceKey(artifact.source);
94
+ if (artifact.role === 'canonical-schema') {
95
+ if (roles.get(key) !== 'canonicalSources')
96
+ fail('artifact-role-invalid');
97
+ }
98
+ else if (artifact.role === 'generated-schema') {
99
+ if (roles.get(key) !== 'generatedFiles' || !Array.isArray(artifact.generatedFrom) || !artifact.generatedFrom.length)
100
+ fail('artifact-role-invalid');
101
+ for (const ref of artifact.generatedFrom)
102
+ if (roles.get(referenceKey(ref)) !== 'canonicalSources')
103
+ fail('artifact-role-invalid');
104
+ }
105
+ else
106
+ fail('artifact-role-invalid');
107
+ }
108
+ }
109
+ export function prepare(bundle, ajv) {
110
+ const registry = new Map();
111
+ const nodes = [];
112
+ const register = (id, location) => {
113
+ const key = normalizeUri(id), previous = registry.get(key);
114
+ if (previous !== undefined && previous !== location)
115
+ fail('resource-id-conflict');
116
+ registry.set(key, location);
117
+ };
118
+ const resources = [];
119
+ const walk = (node, base, location) => {
120
+ if (typeof node === 'boolean')
121
+ return;
122
+ if (!isJsonRecord(node))
123
+ return;
124
+ if ('$schema' in node) {
125
+ const dialect = dialectFor(node.$schema);
126
+ if (!dialect)
127
+ fail('dialect-unsupported');
128
+ if (dialect !== bundle.dialect)
129
+ fail('dialect-conflict');
130
+ // Canonicalize identifiers in the private snapshot, never in the owner's document.
131
+ node.$schema = dialect === 'draft-07' ? DIALECT_07 : DIALECT_2020;
132
+ }
133
+ if ('$id' in node) {
134
+ if (typeof node.$id !== 'string')
135
+ fail('schema-invalid');
136
+ base = absoluteUri(resolve(base, node.$id), true);
137
+ register(base, location);
138
+ node.$id = base;
139
+ }
140
+ for (const key of ['$anchor', '$dynamicAnchor'])
141
+ if (typeof node[key] === 'string')
142
+ register(`${base.replace(/#.*$/, '')}#${node[key]}`, location);
143
+ nodes.push({ node, base });
144
+ if (node.$async === true)
145
+ fail('schema-invalid');
146
+ if (typeof node.format === 'string' && !ajv.formats[node.format])
147
+ fail('schema-invalid');
148
+ for (const key of maps)
149
+ if (isJsonRecord(node[key])) {
150
+ for (const [name, child] of Object.entries(node[key]))
151
+ if (typeof child === 'boolean' || isJsonRecord(child))
152
+ walk(child, base, appendPointer(appendPointer(location, key), name));
153
+ }
154
+ for (const key of singles)
155
+ if (typeof node[key] === 'boolean' || isJsonRecord(node[key]))
156
+ walk(node[key], base, `${location}/${key}`);
157
+ for (const key of [...arrays, 'items']) {
158
+ const children = node[key];
159
+ if (Array.isArray(children))
160
+ children.forEach((child, index) => walk(child, base, `${location}/${key}/${index}`));
161
+ else if (key === 'items' && (typeof children === 'boolean' || isJsonRecord(children)))
162
+ walk(children, base, `${location}/${key}`);
163
+ }
164
+ };
165
+ const supplied = [bundle.root, ...bundle.resources];
166
+ for (let index = 0; index < supplied.length; index++) {
167
+ const resource = supplied[index];
168
+ if (!isJsonRecord(resource) || (typeof resource.document !== 'boolean' && !isJsonRecord(resource.document)))
169
+ fail('resource-invalid');
170
+ const uri = absoluteUri(resource.retrievalUri, false);
171
+ register(uri, `${index}`);
172
+ walk(resource.document, uri, `${index}`);
173
+ resources.push({ uri, document: resource.document });
174
+ }
175
+ // Use retrieval-URI pointers in private refs so Ajv emits unambiguous pointer diagnostics,
176
+ // including refs originally written as relative resource names or anchors.
177
+ for (const { node, base } of nodes)
178
+ if (typeof node.$ref === 'string') {
179
+ const absolute = normalizeUri(resolve(base, node.$ref));
180
+ let location = registry.get(absolute);
181
+ if (location === undefined) {
182
+ const hash = absolute.indexOf('#');
183
+ const resource = registry.get(hash < 0 ? absolute : absolute.slice(0, hash));
184
+ const fragment = hash < 0 ? '' : decodeURIComponent(absolute.slice(hash + 1));
185
+ if (resource !== undefined && (fragment === '' || fragment.startsWith('/')))
186
+ location = resource + fragment;
187
+ }
188
+ if (location !== undefined) {
189
+ const slash = location.indexOf('/'), index = Number(slash < 0 ? location : location.slice(0, slash));
190
+ node.$ref = resources[index].uri + '#' + encodeURIComponent(slash < 0 ? '' : location.slice(slash)).replace(/%2F/g, '/');
191
+ }
192
+ else
193
+ node.$ref = absolute.includes('#') ? absolute : absolute + '#';
194
+ }
195
+ return { root: resources[0].uri, resources, registry, nodes };
196
+ }
197
+ function structuralDiagnostics(errors, scope, rootUri) {
198
+ return Object.freeze(errors.map(error => {
199
+ let dataPath = error.instancePath;
200
+ if (error.keyword === 'required' && typeof error.params.missingProperty === 'string')
201
+ dataPath = appendPointer(dataPath, error.params.missingProperty);
202
+ if (error.keyword === 'additionalProperties' && typeof error.params.additionalProperty === 'string')
203
+ dataPath = appendPointer(dataPath, error.params.additionalProperty);
204
+ const hash = error.schemaPath.indexOf('#');
205
+ const resourceUri = hash > 0 ? error.schemaPath.slice(0, hash) : rootUri;
206
+ const schemaPath = decodeURIComponent(hash >= 0 ? error.schemaPath.slice(hash + 1) : error.schemaPath);
207
+ return Object.freeze({ scope, phase: 'structure', code: 'schema-data-invalid', dataPath, schemaPath, resourceUri, keyword: error.keyword });
208
+ }));
209
+ }
210
+ function makeValidator(validate, scope, dialect, rootUri) {
211
+ return Object.freeze({ identity: scope.identity, dialect, validate(data, context) {
212
+ var _a;
213
+ let currentScope = scope;
214
+ try {
215
+ if (context !== undefined) {
216
+ if (!isJsonRecord(context))
217
+ throw new Error();
218
+ const consumerId = ownString(context, 'consumerId');
219
+ if (consumerId === undefined)
220
+ throw new Error();
221
+ currentScope = Object.freeze({ ...scope, consumerId });
222
+ }
223
+ const input = snapshotJson(data);
224
+ if (validate(input))
225
+ return Object.freeze({ kind: 'valid', diagnostics: Object.freeze([]) });
226
+ const diagnostics = structuralDiagnostics((_a = validate.errors) !== null && _a !== void 0 ? _a : [], currentScope, rootUri);
227
+ if (!diagnostics.length)
228
+ throw new Error();
229
+ return Object.freeze({ kind: 'invalid', diagnostics: diagnostics });
230
+ }
231
+ catch (error) {
232
+ if (error instanceof JsonSnapshotError && error.kind === 'invalid')
233
+ return Object.freeze({ kind: 'invalid', diagnostics: Object.freeze([
234
+ Object.freeze({ scope: currentScope, phase: 'structure', code: 'input-not-json', dataPath: error.dataPath })
235
+ ]) });
236
+ return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([Object.freeze({ scope: currentScope, phase: 'execution', operation: 'structure',
237
+ code: error instanceof JsonSnapshotError && error.kind === 'limit' || error instanceof RangeError ? 'limit-exceeded' : 'engine-failed' })]) });
238
+ }
239
+ } });
240
+ }
241
+ /** Compile an isolated synchronous validator from an immutable private snapshot. */
242
+ export function compileSchemaBundle(input) {
243
+ const scope = scopeFor(input);
244
+ try {
245
+ if (!isJsonRecord(input))
246
+ fail('resource-invalid');
247
+ const declaredDialect = Object.getOwnPropertyDescriptor(input, 'dialect');
248
+ if (!declaredDialect || 'value' in declaredDialect && declaredDialect.value === undefined)
249
+ fail('dialect-required');
250
+ if (!('value' in declaredDialect))
251
+ fail('resource-invalid');
252
+ if (declaredDialect.value !== 'draft-07' && declaredDialect.value !== '2020-12')
253
+ fail('dialect-unsupported');
254
+ const candidate = snapshotJson(input);
255
+ if (!isJsonRecord(candidate) || !isJsonRecord(candidate.identity))
256
+ fail('resource-invalid');
257
+ for (const key of ['ownerId', 'contractId', 'revision', 'variantId'])
258
+ if (typeof candidate.identity[key] !== 'string')
259
+ fail('resource-invalid');
260
+ if (!candidate.dialect)
261
+ fail('dialect-required');
262
+ if (candidate.dialect !== 'draft-07' && candidate.dialect !== '2020-12')
263
+ fail('dialect-unsupported');
264
+ if (!Array.isArray(candidate.resources) || !isJsonRecord(candidate.root))
265
+ fail('resource-invalid');
266
+ const bundle = candidate;
267
+ validateArtifacts(bundle);
268
+ const options = { allErrors: true, coerceTypes: false, useDefaults: false, removeAdditional: false,
269
+ strict: false, logger: false, ownProperties: true };
270
+ const ajv = bundle.dialect === 'draft-07' ? new Ajv(options) : new Ajv2020(options);
271
+ formats.default(ajv);
272
+ const prepared = prepare(bundle, ajv);
273
+ for (const resource of prepared.resources) {
274
+ if (!ajv.validateSchema(resource.document))
275
+ fail('schema-invalid');
276
+ ajv.addSchema(resource.document, resource.uri);
277
+ }
278
+ const validate = ajv.getSchema(prepared.root);
279
+ if (!validate || ('$async' in validate && validate.$async))
280
+ fail('schema-invalid');
281
+ return Object.freeze({ kind: 'compiled', validator: makeValidator(validate, scope, bundle.dialect, prepared.root), diagnostics: Object.freeze([]) });
282
+ }
283
+ catch (error) {
284
+ let code;
285
+ if (error instanceof BundleError)
286
+ code = error.code;
287
+ else if (error instanceof MissingRefError)
288
+ code = 'ref-unresolved';
289
+ else if (error instanceof JsonSnapshotError && error.kind === 'invalid')
290
+ code = 'resource-invalid';
291
+ else if (error instanceof SyntaxError)
292
+ code = 'schema-invalid';
293
+ if (code)
294
+ return Object.freeze({ kind: 'schema-error', diagnostics: Object.freeze([Object.freeze({ scope, phase: 'compile', code })]) });
295
+ return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([Object.freeze({ scope, phase: 'execution', operation: 'compile',
296
+ code: error instanceof JsonSnapshotError && error.kind === 'limit' || error instanceof RangeError ? 'limit-exceeded' : 'engine-failed' })]) });
297
+ }
298
+ }
@@ -0,0 +1,10 @@
1
+ import type { JsonPointer, SchemaJsonValue } from './schema-types.js';
2
+ export declare class JsonSnapshotError extends Error {
3
+ readonly kind: 'invalid' | 'limit';
4
+ readonly dataPath: JsonPointer;
5
+ constructor(kind: 'invalid' | 'limit', dataPath: JsonPointer);
6
+ }
7
+ export declare function appendPointer(path: string, key: string): JsonPointer;
8
+ /** Copy JSON values without invoking accessors, toJSON, or input iterators. */
9
+ export declare function snapshotJson(value: unknown): SchemaJsonValue;
10
+ export declare function isJsonRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1,68 @@
1
+ export class JsonSnapshotError extends Error {
2
+ constructor(kind, dataPath) {
3
+ super('JSON snapshot failed.');
4
+ this.kind = kind;
5
+ this.dataPath = dataPath;
6
+ }
7
+ }
8
+ export function appendPointer(path, key) {
9
+ return `${path}/${key.replace(/~/g, '~0').replace(/\//g, '~1')}`;
10
+ }
11
+ /** Copy JSON values without invoking accessors, toJSON, or input iterators. */
12
+ export function snapshotJson(value) {
13
+ const active = new WeakSet();
14
+ let remaining = 100000;
15
+ const visit = (input, path, depth) => {
16
+ if (--remaining < 0 || depth > 128)
17
+ throw new JsonSnapshotError('limit', path);
18
+ if (input === null || typeof input === 'string' || typeof input === 'boolean')
19
+ return input;
20
+ if (typeof input === 'number' && Number.isFinite(input))
21
+ return input;
22
+ if (typeof input !== 'object' || input === null || active.has(input))
23
+ throw new JsonSnapshotError('invalid', path);
24
+ const array = Array.isArray(input);
25
+ const prototype = Object.getPrototypeOf(input);
26
+ if (!array && prototype !== null && prototype !== Object.prototype)
27
+ throw new JsonSnapshotError('invalid', path);
28
+ if (Object.getOwnPropertySymbols(input).length)
29
+ throw new JsonSnapshotError('invalid', path);
30
+ active.add(input);
31
+ try {
32
+ if (array) {
33
+ const length = input.length;
34
+ if (!Number.isSafeInteger(length) || length > remaining)
35
+ throw new JsonSnapshotError('limit', path);
36
+ const result = [];
37
+ for (let i = 0; i < length; i++) {
38
+ const child = appendPointer(path, String(i));
39
+ const descriptor = Object.getOwnPropertyDescriptor(input, i);
40
+ if (!descriptor || !('value' in descriptor))
41
+ throw new JsonSnapshotError('invalid', child);
42
+ result.push(visit(descriptor.value, child, depth + 1));
43
+ }
44
+ for (const key of Object.keys(input)) {
45
+ if (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= length)
46
+ throw new JsonSnapshotError('invalid', appendPointer(path, key));
47
+ }
48
+ return result;
49
+ }
50
+ const result = {};
51
+ for (const key of Object.keys(input)) {
52
+ const child = appendPointer(path, key);
53
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
54
+ if (!descriptor || !('value' in descriptor))
55
+ throw new JsonSnapshotError('invalid', child);
56
+ Object.defineProperty(result, key, { value: visit(descriptor.value, child, depth + 1), enumerable: true, writable: true, configurable: true });
57
+ }
58
+ return result;
59
+ }
60
+ finally {
61
+ active.delete(input);
62
+ }
63
+ };
64
+ return visit(value, '', 0);
65
+ }
66
+ export function isJsonRecord(value) {
67
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
68
+ }
@@ -0,0 +1,161 @@
1
+ export interface ValidationIssue {
2
+ readonly message: string;
3
+ readonly path?: string;
4
+ }
5
+ export type NonEmpty<T> = readonly [T, ...T[]];
6
+ export type JsonPointer = '' | `/${string}`;
7
+ export type SchemaDialect = 'draft-07' | '2020-12';
8
+ export type SchemaJsonValue = null | string | number | boolean | readonly SchemaJsonValue[] | {
9
+ readonly [key: string]: SchemaJsonValue;
10
+ };
11
+ export type SchemaJsonObject = {
12
+ readonly [key: string]: SchemaJsonValue;
13
+ };
14
+ export type SchemaDocument = boolean | SchemaJsonObject;
15
+ export type JsonSettings<T> = T extends null | string | number | boolean ? T : T extends (...args: never[]) => unknown ? never : T extends abstract new (...args: never[]) => unknown ? never : T extends readonly unknown[] ? {
16
+ readonly [K in keyof T]: JsonSettings<T[K]>;
17
+ } : T extends object ? {
18
+ readonly [K in keyof T]: JsonSettings<T[K]>;
19
+ } : never;
20
+ export interface SchemaIdentity {
21
+ readonly ownerId: string;
22
+ readonly contractId: string;
23
+ readonly revision: string;
24
+ readonly variantId: string;
25
+ }
26
+ export interface SchemaSourceReference {
27
+ readonly repository: string;
28
+ readonly path: string;
29
+ readonly symbol?: string;
30
+ readonly version?: string;
31
+ readonly sha256?: string;
32
+ }
33
+ export interface SchemaProvenance {
34
+ readonly canonicalSources: NonEmpty<SchemaSourceReference>;
35
+ readonly generatedFiles: readonly SchemaSourceReference[];
36
+ readonly referenceSchemas: readonly SchemaSourceReference[];
37
+ readonly generationSources: readonly SchemaSourceReference[];
38
+ }
39
+ export type SchemaArtifact = {
40
+ readonly role: 'canonical-schema';
41
+ readonly source: SchemaSourceReference;
42
+ } | {
43
+ readonly role: 'generated-schema';
44
+ readonly source: SchemaSourceReference;
45
+ readonly generatedFrom: NonEmpty<SchemaSourceReference>;
46
+ };
47
+ export interface SchemaResource {
48
+ readonly retrievalUri: string;
49
+ readonly document: SchemaDocument;
50
+ readonly artifact: SchemaArtifact;
51
+ }
52
+ export interface SchemaBundle {
53
+ readonly identity: SchemaIdentity;
54
+ readonly dialect: SchemaDialect;
55
+ readonly root: SchemaResource;
56
+ readonly resources: readonly SchemaResource[];
57
+ readonly provenance: SchemaProvenance;
58
+ }
59
+ export interface SchemaDiagnosticScope {
60
+ readonly identity: SchemaIdentity;
61
+ readonly consumerId?: string;
62
+ }
63
+ export interface SchemaDiagnosticBase {
64
+ readonly scope: SchemaDiagnosticScope;
65
+ readonly resourceUri?: string;
66
+ readonly schemaPath?: JsonPointer;
67
+ }
68
+ export type CompileDiagnosticCode = 'dialect-required' | 'dialect-unsupported' | 'dialect-conflict' | 'resource-invalid' | 'resource-id-conflict' | 'ref-unresolved' | 'schema-invalid' | 'artifact-role-invalid';
69
+ export type CompileDiagnostic = SchemaDiagnosticBase & {
70
+ readonly phase: 'compile';
71
+ readonly code: CompileDiagnosticCode;
72
+ };
73
+ export type InputDiagnostic = SchemaDiagnosticBase & ({
74
+ readonly phase: 'decode';
75
+ readonly code: 'input-format-invalid' | 'variant-unrecognized';
76
+ readonly dataPath: JsonPointer;
77
+ } | {
78
+ readonly phase: 'structure';
79
+ readonly code: 'input-not-json' | 'schema-data-invalid';
80
+ readonly dataPath: JsonPointer;
81
+ readonly keyword?: string;
82
+ } | {
83
+ readonly phase: 'semantic';
84
+ readonly code: 'owner-input-invalid';
85
+ readonly dataPath: JsonPointer;
86
+ readonly ruleId: string;
87
+ });
88
+ export type CompileExecutionDiagnostic = SchemaDiagnosticBase & {
89
+ readonly phase: 'execution';
90
+ readonly operation: 'compile';
91
+ readonly code: 'engine-failed' | 'limit-exceeded';
92
+ };
93
+ export type DecodeExecutionDiagnostic = SchemaDiagnosticBase & {
94
+ readonly phase: 'execution';
95
+ readonly operation: 'decode';
96
+ readonly code: 'engine-failed' | 'limit-exceeded';
97
+ };
98
+ export type StructuralExecutionDiagnostic = SchemaDiagnosticBase & {
99
+ readonly phase: 'execution';
100
+ readonly operation: 'structure';
101
+ readonly code: 'engine-failed' | 'limit-exceeded';
102
+ };
103
+ export type OwnerExecutionDiagnostic = SchemaDiagnosticBase & {
104
+ readonly phase: 'execution';
105
+ readonly operation: 'semantic';
106
+ readonly code: 'owner-validation-failed' | 'owner-context-invalid' | 'async-owner-unsupported' | 'owner-result-invalid' | 'engine-failed' | 'limit-exceeded';
107
+ };
108
+ export type ExecutionDiagnostic = CompileExecutionDiagnostic | DecodeExecutionDiagnostic | StructuralExecutionDiagnostic | OwnerExecutionDiagnostic;
109
+ export type ValidationExecutionDiagnostic = DecodeExecutionDiagnostic | StructuralExecutionDiagnostic | OwnerExecutionDiagnostic;
110
+ export type SchemaDiagnostic = CompileDiagnostic | InputDiagnostic | ExecutionDiagnostic;
111
+ export type SchemaValidationResult<D extends InputDiagnostic = InputDiagnostic, E extends ValidationExecutionDiagnostic = ValidationExecutionDiagnostic> = {
112
+ readonly kind: 'valid';
113
+ readonly diagnostics: readonly [];
114
+ } | {
115
+ readonly kind: 'invalid';
116
+ readonly diagnostics: NonEmpty<D>;
117
+ } | {
118
+ readonly kind: 'execution-error';
119
+ readonly diagnostics: NonEmpty<E>;
120
+ };
121
+ export type StructuralValidationResult = SchemaValidationResult<Extract<InputDiagnostic, {
122
+ phase: 'structure';
123
+ }>, StructuralExecutionDiagnostic>;
124
+ export type SchemaInputValidationResult = SchemaValidationResult<Extract<InputDiagnostic, {
125
+ phase: 'structure' | 'semantic';
126
+ }>, StructuralExecutionDiagnostic | OwnerExecutionDiagnostic>;
127
+ export interface CompiledSchema {
128
+ readonly identity: SchemaIdentity;
129
+ readonly dialect: SchemaDialect;
130
+ readonly validate: (data: SchemaJsonValue, context?: {
131
+ readonly consumerId: string;
132
+ }) => StructuralValidationResult;
133
+ }
134
+ export type SchemaCompileResult = {
135
+ readonly kind: 'compiled';
136
+ readonly validator: CompiledSchema;
137
+ readonly diagnostics: readonly [];
138
+ } | {
139
+ readonly kind: 'schema-error';
140
+ readonly diagnostics: NonEmpty<CompileDiagnostic>;
141
+ } | {
142
+ readonly kind: 'execution-error';
143
+ readonly diagnostics: NonEmpty<CompileExecutionDiagnostic>;
144
+ };
145
+ export interface SchemaValidationContext<C = null> {
146
+ readonly consumerId: string;
147
+ readonly nowMs: number;
148
+ readonly settings: JsonSettings<C>;
149
+ }
150
+ export interface OwnerInputIssue {
151
+ readonly dataPath: JsonPointer;
152
+ readonly ruleId: string;
153
+ }
154
+ export type OwnerValidationResult = {
155
+ readonly kind: 'valid';
156
+ readonly issues: readonly [];
157
+ } | {
158
+ readonly kind: 'invalid';
159
+ readonly issues: NonEmpty<OwnerInputIssue>;
160
+ };
161
+ export type SyncOwnerValidator<C = null> = (data: SchemaJsonValue, context: SchemaValidationContext<C>) => OwnerValidationResult;
@@ -0,0 +1,2 @@
1
+ // Shared schema contracts. Engine and owner composition implementations are provided separately.
2
+ export {};