schemair 0.1.0 → 0.3.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/README.md +542 -32
- package/dist/authoring/index.cjs +13 -350
- package/dist/authoring/index.d.cts +2 -90
- package/dist/authoring/index.d.ts +2 -90
- package/dist/authoring/index.js +2 -304
- package/dist/authoring-Dnrg5TXm.cjs +389 -0
- package/dist/authoring-DyJNYs5o.js +362 -0
- package/dist/chunk-bFfwrPtJ.js +18 -0
- package/dist/compatibility/index.cjs +43 -0
- package/dist/compatibility/index.d.cts +2 -0
- package/dist/compatibility/index.d.ts +2 -0
- package/dist/compatibility/index.js +42 -0
- package/dist/definition/index.cjs +10 -2
- package/dist/definition/index.d.cts +3 -21
- package/dist/definition/index.d.ts +3 -21
- package/dist/definition/index.js +2 -2
- package/dist/definition-BH_LzXGc.js +1115 -0
- package/dist/definition-BLLBpLKv.cjs +1187 -0
- package/dist/index-1sDuqu05.d.ts +373 -0
- package/dist/index-BFSMT_2d.d.ts +53 -0
- package/dist/index-BGz7L_Fq.d.cts +73 -0
- package/dist/index-BTtdqi_M.d.ts +418 -0
- package/dist/index-BfbClf6R.d.cts +20 -0
- package/dist/index-CK7Is7vy.d.cts +420 -0
- package/dist/index-CUGhtuwA.d.cts +53 -0
- package/dist/index-CV8EZQy8.d.ts +92 -0
- package/dist/index-CzNxjqVg.d.cts +47 -0
- package/dist/index-DGHMUZ5m.d.cts +92 -0
- package/dist/{index-eK2Xgrwp.d.ts → index-DxvfPmw6.d.ts} +13 -9
- package/dist/index-T6nFhvGQ.d.ts +20 -0
- package/dist/{index-TACcK_mi.d.cts → index-ba_52Yu9.d.cts} +13 -9
- package/dist/index-hrcM9oJe.d.ts +47 -0
- package/dist/index-i7Au-FQ6.d.cts +373 -0
- package/dist/index-mu1znI1F.d.ts +73 -0
- package/dist/index.cjs +70 -58
- package/dist/index.d.cts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +10 -6
- package/dist/integrations/json-schema/index.cjs +328 -0
- package/dist/integrations/json-schema/index.d.cts +53 -0
- package/dist/integrations/json-schema/index.d.ts +53 -0
- package/dist/integrations/json-schema/index.js +324 -0
- package/dist/integrations/standard-schema/index.cjs +32 -0
- package/dist/integrations/standard-schema/index.d.cts +22 -0
- package/dist/integrations/standard-schema/index.d.ts +22 -0
- package/dist/integrations/standard-schema/index.js +32 -0
- package/dist/payload/index.cjs +45 -82
- package/dist/payload/index.d.cts +2 -56
- package/dist/payload/index.d.ts +2 -56
- package/dist/payload/index.js +45 -81
- package/dist/relations/index.cjs +3 -4
- package/dist/relations/index.d.cts +2 -2
- package/dist/relations/index.d.ts +2 -2
- package/dist/relations/index.js +2 -2
- package/dist/{relations-CMz_0nf4.cjs → relations-NUVJkAG8.cjs} +143 -211
- package/dist/{relations-By0bK3UK.js → relations-QtnCRW9x.js} +140 -202
- package/dist/resolve/index.cjs +308 -0
- package/dist/resolve/index.d.cts +2 -0
- package/dist/resolve/index.d.ts +2 -0
- package/dist/resolve/index.js +305 -0
- package/dist/schema-ref-key-BHVFVfj1.cjs +26 -0
- package/dist/schema-ref-key-DR__tPr-.js +14 -0
- package/dist/spec/index.cjs +9 -0
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +3 -1
- package/dist/standard/index.cjs +810 -0
- package/dist/standard/index.d.cts +3 -0
- package/dist/standard/index.d.ts +3 -0
- package/dist/standard/index.js +777 -0
- package/dist/standard-BGMgbbEQ.js +471 -0
- package/dist/standard-etPFZDJI.cjs +513 -0
- package/dist/type-interpreter/index.cjs +3 -7
- package/dist/type-interpreter/index.d.cts +2 -99
- package/dist/type-interpreter/index.d.ts +2 -99
- package/dist/type-interpreter/index.js +2 -2
- package/dist/{type-interpreter-Ck0VDhzn.js → type-interpreter-BA8gcLUU.js} +58 -560
- package/dist/{type-interpreter-DAIBUqKf.cjs → type-interpreter-D8xETgq1.cjs} +63 -589
- package/package.json +60 -5
- package/dist/definition-BXj3T94L.js +0 -467
- package/dist/definition-DSZZTz4e.cjs +0 -473
- package/dist/index-CEzHfht2.d.ts +0 -269
- package/dist/index-rzkFjgrt.d.cts +0 -269
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { i as STANDARD_CONSTRAINT_PATHS, o as STANDARD_SEMANTIC_PATHS } from "../../standard-BGMgbbEQ.js";
|
|
2
|
+
import { pathKey } from "../../standard/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/integrations/json-schema/index.ts
|
|
5
|
+
function diagnostic(context, code, message, path, severity = "warning") {
|
|
6
|
+
context.diagnostics.push({
|
|
7
|
+
code,
|
|
8
|
+
message,
|
|
9
|
+
path,
|
|
10
|
+
severity
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function mergeKeyword(target, source) {
|
|
14
|
+
if (source) Object.assign(target, source);
|
|
15
|
+
}
|
|
16
|
+
function extension(target, value) {
|
|
17
|
+
const existing = target["x-schemair"];
|
|
18
|
+
target["x-schemair"] = {
|
|
19
|
+
...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {},
|
|
20
|
+
...value,
|
|
21
|
+
version: 1
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function standardSemantic(path, node) {
|
|
25
|
+
const key = pathKey(path);
|
|
26
|
+
const stringFormats = {
|
|
27
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.date)]: "date",
|
|
28
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.datetime)]: "date-time",
|
|
29
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.datetimeIso)]: "date-time",
|
|
30
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.email)]: "email",
|
|
31
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.url)]: "uri",
|
|
32
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.ipV4)]: "ipv4",
|
|
33
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.ipV6)]: "ipv6",
|
|
34
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.uuid)]: "uuid",
|
|
35
|
+
[pathKey(STANDARD_SEMANTIC_PATHS.string.uuidV4)]: "uuid"
|
|
36
|
+
};
|
|
37
|
+
if (node.name === "string" && stringFormats[key]) return { format: stringFormats[key] };
|
|
38
|
+
if (node.name === "number" && key === pathKey(STANDARD_SEMANTIC_PATHS.number.integer)) return { type: "integer" };
|
|
39
|
+
if (node.name === "number" && key === pathKey(STANDARD_SEMANTIC_PATHS.number.safeInteger)) return {
|
|
40
|
+
type: "integer",
|
|
41
|
+
minimum: -9007199254740991,
|
|
42
|
+
maximum: 9007199254740991
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function standardConstraint(constraint, target) {
|
|
46
|
+
const key = pathKey(constraint.constraintPath);
|
|
47
|
+
const value = constraint.args;
|
|
48
|
+
switch (key) {
|
|
49
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.string.minLength): return typeof value === "number" ? { minLength: value } : void 0;
|
|
50
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.string.maxLength): return typeof value === "number" ? { maxLength: value } : void 0;
|
|
51
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.string.pattern): return typeof value === "string" ? { pattern: value } : void 0;
|
|
52
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.number.minimum): return typeof value === "number" ? { minimum: value } : void 0;
|
|
53
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.number.maximum): return typeof value === "number" ? { maximum: value } : void 0;
|
|
54
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.number.exclusiveMinimum): return typeof value === "number" ? target === "draft-2020-12" ? { exclusiveMinimum: value } : {
|
|
55
|
+
minimum: value,
|
|
56
|
+
exclusiveMinimum: true
|
|
57
|
+
} : void 0;
|
|
58
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.number.exclusiveMaximum): return typeof value === "number" ? target === "draft-2020-12" ? { exclusiveMaximum: value } : {
|
|
59
|
+
maximum: value,
|
|
60
|
+
exclusiveMaximum: true
|
|
61
|
+
} : void 0;
|
|
62
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.number.multipleOf): return typeof value === "number" ? { multipleOf: value } : void 0;
|
|
63
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.array.minItems): return typeof value === "number" ? { minItems: value } : void 0;
|
|
64
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.array.maxItems): return typeof value === "number" ? { maxItems: value } : void 0;
|
|
65
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.array.uniqueItems): return typeof value === "boolean" ? { uniqueItems: value } : void 0;
|
|
66
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.record.minProperties): return typeof value === "number" ? { minProperties: value } : void 0;
|
|
67
|
+
case pathKey(STANDARD_CONSTRAINT_PATHS.record.maxProperties): return typeof value === "number" ? { maxProperties: value } : void 0;
|
|
68
|
+
default: return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function applyMetadata(target, node, context, path) {
|
|
72
|
+
if ("semanticPath" in node && node.semanticPath) {
|
|
73
|
+
const custom = context.options.semanticMapper?.(node.semanticPath, node);
|
|
74
|
+
const mapped = custom ?? standardSemantic(node.semanticPath, node);
|
|
75
|
+
if (mapped) {
|
|
76
|
+
mergeKeyword(target, mapped);
|
|
77
|
+
extension(target, { semanticPath: node.semanticPath });
|
|
78
|
+
if (custom === void 0) diagnostic(context, "semantic.lossy", `Semantic path ${node.semanticPath.join("/")} is approximated by JSON Schema keywords`, path);
|
|
79
|
+
} else {
|
|
80
|
+
extension(target, { semanticPath: node.semanticPath });
|
|
81
|
+
diagnostic(context, "semantic.unmapped", `Semantic path ${node.semanticPath.join("/")} has no lossless JSON Schema mapping`, path);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if ("constraints" in node && node.constraints) for (const constraint of node.constraints) {
|
|
85
|
+
const mapped = context.options.constraintMapper?.(constraint, node) ?? standardConstraint(constraint, context.target);
|
|
86
|
+
if (mapped) mergeKeyword(target, mapped);
|
|
87
|
+
else {
|
|
88
|
+
const prior = target["x-schemair"];
|
|
89
|
+
const meta = prior && typeof prior === "object" && !Array.isArray(prior) ? prior : {};
|
|
90
|
+
extension(target, { constraints: [...Array.isArray(meta.constraints) ? meta.constraints : [], {
|
|
91
|
+
constraintPath: [...constraint.constraintPath],
|
|
92
|
+
...constraint.args === void 0 ? {} : { args: constraint.args }
|
|
93
|
+
}] });
|
|
94
|
+
diagnostic(context, "constraint.unmapped", `Constraint ${constraint.constraintPath.join("/")} has no lossless JSON Schema mapping`, path);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function project(node, context, path) {
|
|
99
|
+
if (context.stack.includes(node)) {
|
|
100
|
+
diagnostic(context, "schema.cycle", "Cyclic schema graph requires an explicit ref strategy", path, "error");
|
|
101
|
+
const cycle = {};
|
|
102
|
+
extension(cycle, { selfRef: true });
|
|
103
|
+
return cycle;
|
|
104
|
+
}
|
|
105
|
+
context.stack.push(node);
|
|
106
|
+
let result;
|
|
107
|
+
switch (node.kind) {
|
|
108
|
+
case "primitive":
|
|
109
|
+
if (context.target === "openapi-3.0" && node.name === "null") {
|
|
110
|
+
result = {};
|
|
111
|
+
extension(result, { original: {
|
|
112
|
+
kind: "primitive",
|
|
113
|
+
name: "null"
|
|
114
|
+
} });
|
|
115
|
+
diagnostic(context, "openapi.null.unsupported", "OpenAPI 3.0 Schema Objects cannot represent a standalone null type", path, "error");
|
|
116
|
+
} else result = { type: node.name === "null" ? "null" : node.name };
|
|
117
|
+
applyMetadata(result, node, context, path);
|
|
118
|
+
break;
|
|
119
|
+
case "literal":
|
|
120
|
+
if (context.target === "openapi-3.0") {
|
|
121
|
+
result = { enum: [node.literal] };
|
|
122
|
+
extension(result, { literal: node.literal });
|
|
123
|
+
diagnostic(context, "openapi.literal.lossy", "OpenAPI 3.0 has no const keyword; the literal is projected as a single-value enum", path);
|
|
124
|
+
} else result = { const: node.literal };
|
|
125
|
+
break;
|
|
126
|
+
case "literalUnion":
|
|
127
|
+
result = { enum: [...node.values] };
|
|
128
|
+
extension(result, { union: {
|
|
129
|
+
kind: "literalUnion",
|
|
130
|
+
values: [...node.values]
|
|
131
|
+
} });
|
|
132
|
+
break;
|
|
133
|
+
case "nullable": {
|
|
134
|
+
const inner = project(node.value, context, [...path, "value"]);
|
|
135
|
+
if (context.target === "openapi-3.0") {
|
|
136
|
+
result = {
|
|
137
|
+
...inner,
|
|
138
|
+
nullable: true
|
|
139
|
+
};
|
|
140
|
+
extension(result, { union: {
|
|
141
|
+
kind: "nullable",
|
|
142
|
+
value: true
|
|
143
|
+
} });
|
|
144
|
+
diagnostic(context, "openapi.nullable.lossy", "OpenAPI 3.0 nullable is preserved as an extension because it is not equivalent to JSON Schema null union semantics", path);
|
|
145
|
+
} else result = { anyOf: [inner, { type: "null" }] };
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case "array":
|
|
149
|
+
result = {
|
|
150
|
+
type: "array",
|
|
151
|
+
items: project(node.elementSchema, context, [...path, "elementSchema"])
|
|
152
|
+
};
|
|
153
|
+
applyMetadata(result, node, context, path);
|
|
154
|
+
break;
|
|
155
|
+
case "tuple":
|
|
156
|
+
if (context.target === "draft-07") result = {
|
|
157
|
+
type: "array",
|
|
158
|
+
items: node.elementSchemas.map((item, index) => project(item, context, [
|
|
159
|
+
...path,
|
|
160
|
+
"elementSchemas",
|
|
161
|
+
index
|
|
162
|
+
])),
|
|
163
|
+
additionalItems: false,
|
|
164
|
+
minItems: node.elementSchemas.length,
|
|
165
|
+
maxItems: node.elementSchemas.length
|
|
166
|
+
};
|
|
167
|
+
else if (context.target === "openapi-3.0") {
|
|
168
|
+
const elements = node.elementSchemas.map((item, index) => project(item, context, [
|
|
169
|
+
...path,
|
|
170
|
+
"elementSchemas",
|
|
171
|
+
index
|
|
172
|
+
]));
|
|
173
|
+
result = {
|
|
174
|
+
type: "array",
|
|
175
|
+
items: {}
|
|
176
|
+
};
|
|
177
|
+
extension(result, { union: {
|
|
178
|
+
kind: "tuple",
|
|
179
|
+
length: node.elementSchemas.length,
|
|
180
|
+
elementSchemas: elements
|
|
181
|
+
} });
|
|
182
|
+
diagnostic(context, "openapi.tuple.lossy", "OpenAPI 3.0 has no tuple form; tuple position schemas are retained only in x-schemair", path);
|
|
183
|
+
} else result = {
|
|
184
|
+
type: "array",
|
|
185
|
+
prefixItems: node.elementSchemas.map((item, index) => project(item, context, [
|
|
186
|
+
...path,
|
|
187
|
+
"elementSchemas",
|
|
188
|
+
index
|
|
189
|
+
])),
|
|
190
|
+
items: false,
|
|
191
|
+
minItems: node.elementSchemas.length,
|
|
192
|
+
maxItems: node.elementSchemas.length
|
|
193
|
+
};
|
|
194
|
+
break;
|
|
195
|
+
case "record": {
|
|
196
|
+
const properties = {};
|
|
197
|
+
const required = [];
|
|
198
|
+
for (const field of node.fields) {
|
|
199
|
+
properties[field.key] = project(field.schema, context, [
|
|
200
|
+
...path,
|
|
201
|
+
"fields",
|
|
202
|
+
field.key
|
|
203
|
+
]);
|
|
204
|
+
if (field.required) required.push(field.key);
|
|
205
|
+
}
|
|
206
|
+
result = {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties
|
|
209
|
+
};
|
|
210
|
+
if (required.length) result.required = required;
|
|
211
|
+
if (node.additionalFields === void 0) result.additionalProperties = false;
|
|
212
|
+
else if (node.additionalFields === true) result.additionalProperties = true;
|
|
213
|
+
else result.additionalProperties = project(node.additionalFields, context, [...path, "additionalFields"]);
|
|
214
|
+
applyMetadata(result, node, context, path);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case "taggedUnion":
|
|
218
|
+
result = { oneOf: node.variants.map((variant, index) => project(variant.schema, context, [
|
|
219
|
+
...path,
|
|
220
|
+
"variants",
|
|
221
|
+
index
|
|
222
|
+
])) };
|
|
223
|
+
extension(result, { union: {
|
|
224
|
+
kind: "taggedUnion",
|
|
225
|
+
discriminator: node.discriminator,
|
|
226
|
+
variants: node.variants.map((variant) => ({
|
|
227
|
+
key: variant.key,
|
|
228
|
+
description: variant.description ?? null
|
|
229
|
+
}))
|
|
230
|
+
} });
|
|
231
|
+
diagnostic(context, "union.tagged.lossy", "SchemaIR tagged-union keys are not JSON Schema validation semantics", path);
|
|
232
|
+
break;
|
|
233
|
+
case "ref": {
|
|
234
|
+
const reference = context.options.refStrategy?.(node.refPath);
|
|
235
|
+
result = reference ? { $ref: reference } : {};
|
|
236
|
+
if (!reference) {
|
|
237
|
+
extension(result, { refPath: node.refPath });
|
|
238
|
+
diagnostic(context, "ref.unresolved", `No JSON Schema ref strategy resolved ${node.refPath.join("/")}`, path, "error");
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case "selfRef":
|
|
243
|
+
result = {};
|
|
244
|
+
extension(result, { selfRef: true });
|
|
245
|
+
diagnostic(context, "ref.self", "selfRef needs a caller-owned JSON Schema anchor strategy", path, "error");
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
context.stack.pop();
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
function fidelity(diagnostics) {
|
|
252
|
+
if (diagnostics.some((item) => item.severity === "error")) return "unsupported";
|
|
253
|
+
if (diagnostics.length) return "lossy";
|
|
254
|
+
return "exact";
|
|
255
|
+
}
|
|
256
|
+
/** Project a SchemaNode into JSON Schema Draft 2020-12 without hiding loss. */
|
|
257
|
+
function schemaNodeToJsonSchema(node, options = {}) {
|
|
258
|
+
const target = options.target ?? "draft-2020-12";
|
|
259
|
+
if (target === "openapi-3.0") return schemaNodeToOpenApiSchema(node, options);
|
|
260
|
+
const context = {
|
|
261
|
+
options,
|
|
262
|
+
target,
|
|
263
|
+
diagnostics: [],
|
|
264
|
+
stack: []
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
schema: project(node, context, []),
|
|
268
|
+
fidelity: fidelity(context.diagnostics),
|
|
269
|
+
diagnostics: context.diagnostics
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/** Project a SchemaNode to an OpenAPI 3.0 Schema Object. */
|
|
273
|
+
function schemaNodeToOpenApiSchema(node, options = {}) {
|
|
274
|
+
const context = {
|
|
275
|
+
options,
|
|
276
|
+
target: "openapi-3.0",
|
|
277
|
+
diagnostics: [],
|
|
278
|
+
stack: []
|
|
279
|
+
};
|
|
280
|
+
return {
|
|
281
|
+
schema: project(node, context, []),
|
|
282
|
+
fidelity: fidelity(context.diagnostics),
|
|
283
|
+
diagnostics: context.diagnostics
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/** Alias emphasizing that this is a diagnostic-bearing projection operation. */
|
|
287
|
+
const schemaNodeToJsonSchemaWithDiagnostics = schemaNodeToJsonSchema;
|
|
288
|
+
/**
|
|
289
|
+
* Expose a runtime SchemaIR declaration through Standard JSON Schema V1.
|
|
290
|
+
*
|
|
291
|
+
* SchemaIR declarations do not perform input/output transformations, so both
|
|
292
|
+
* converter methods project the same contract. Lossy projections are allowed
|
|
293
|
+
* because their preserved metadata remains in `x-schemair`; unsupported
|
|
294
|
+
* projections throw as required by the Standard JSON Schema protocol.
|
|
295
|
+
*/
|
|
296
|
+
function schemaIRToStandardJsonSchema(node, options = {}) {
|
|
297
|
+
const convert = (target) => {
|
|
298
|
+
const { target: _ignoredTarget, ...projectionOptions } = options;
|
|
299
|
+
const projectionTarget = target === "openapi-3.0" || target === "draft-2020-12" || target === "draft-07" ? target : void 0;
|
|
300
|
+
const result = projectionTarget === "openapi-3.0" ? schemaNodeToOpenApiSchema(node, projectionOptions) : projectionTarget ? schemaNodeToJsonSchema(node, {
|
|
301
|
+
...projectionOptions,
|
|
302
|
+
target: projectionTarget
|
|
303
|
+
}) : void 0;
|
|
304
|
+
if (!result) throw new Error(`SchemaIR Standard JSON Schema adapter does not support target ${target}`);
|
|
305
|
+
if (result.fidelity === "unsupported") {
|
|
306
|
+
const details = result.diagnostics.map((item) => item.message).join("; ");
|
|
307
|
+
throw new Error(`SchemaIR cannot be represented as JSON Schema: ${details}`);
|
|
308
|
+
}
|
|
309
|
+
return result.schema;
|
|
310
|
+
};
|
|
311
|
+
return { "~standard": {
|
|
312
|
+
version: 1,
|
|
313
|
+
vendor: "schemair",
|
|
314
|
+
jsonSchema: {
|
|
315
|
+
input: ({ target }) => convert(target),
|
|
316
|
+
output: ({ target }) => convert(target)
|
|
317
|
+
}
|
|
318
|
+
} };
|
|
319
|
+
}
|
|
320
|
+
/** Uppercase acronym alias matching the protocol name. */
|
|
321
|
+
const schemaIRToStandardJSONSchema = schemaIRToStandardJsonSchema;
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
export { schemaIRToStandardJSONSchema, schemaIRToStandardJsonSchema, schemaNodeToJsonSchema, schemaNodeToJsonSchemaWithDiagnostics, schemaNodeToOpenApiSchema };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const require_payload_index = require('../../payload/index.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/integrations/standard-schema/index.ts
|
|
4
|
+
function toStandardIssue(issue) {
|
|
5
|
+
const path = issue.path.length > 0 ? [...issue.path] : void 0;
|
|
6
|
+
return path === void 0 ? { message: issue.message } : {
|
|
7
|
+
message: issue.message,
|
|
8
|
+
path
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Expose a serializable SchemaIR declaration through the Standard Schema
|
|
13
|
+
* runtime protocol. Validation is synchronous; load external refs and policy
|
|
14
|
+
* data before constructing the adapter.
|
|
15
|
+
*
|
|
16
|
+
* The adapter never transforms a payload and intentionally does not expose
|
|
17
|
+
* `~standard.types` because the declaration is runtime data.
|
|
18
|
+
*/
|
|
19
|
+
function schemaIRToStandardSchema(schema, options = {}) {
|
|
20
|
+
return { "~standard": {
|
|
21
|
+
version: 1,
|
|
22
|
+
vendor: "schemair",
|
|
23
|
+
validate: (value) => {
|
|
24
|
+
const result = require_payload_index.validatePayload(value, schema, options);
|
|
25
|
+
if (result.status === "accepted") return { value };
|
|
26
|
+
return { issues: result.issues.map(toStandardIssue) };
|
|
27
|
+
}
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
exports.schemaIRToStandardSchema = schemaIRToStandardSchema;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ct as SchemaNode } from "../../index-i7Au-FQ6.cjs";
|
|
2
|
+
import { s as PayloadValidationContext } from "../../index-CzNxjqVg.cjs";
|
|
3
|
+
import { StandardSchemaV1, StandardSchemaV1 as StandardSchemaV1$1 } from "@standard-schema/spec";
|
|
4
|
+
|
|
5
|
+
//#region src/integrations/standard-schema/index.d.ts
|
|
6
|
+
/** Options captured by a Standard Schema adapter at construction time. */
|
|
7
|
+
type StandardSchemaAdapterOptions = PayloadValidationContext;
|
|
8
|
+
/** Runtime-only Standard Schema surface for a dynamic SchemaIR declaration. */
|
|
9
|
+
type SchemaIRStandardSchema = {
|
|
10
|
+
readonly '~standard': Omit<StandardSchemaV1$1.Props, 'types'>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Expose a serializable SchemaIR declaration through the Standard Schema
|
|
14
|
+
* runtime protocol. Validation is synchronous; load external refs and policy
|
|
15
|
+
* data before constructing the adapter.
|
|
16
|
+
*
|
|
17
|
+
* The adapter never transforms a payload and intentionally does not expose
|
|
18
|
+
* `~standard.types` because the declaration is runtime data.
|
|
19
|
+
*/
|
|
20
|
+
declare function schemaIRToStandardSchema(schema: SchemaNode, options?: StandardSchemaAdapterOptions): SchemaIRStandardSchema;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { SchemaIRStandardSchema, StandardSchemaAdapterOptions, type StandardSchemaV1, schemaIRToStandardSchema };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ct as SchemaNode } from "../../index-1sDuqu05.js";
|
|
2
|
+
import { s as PayloadValidationContext } from "../../index-hrcM9oJe.js";
|
|
3
|
+
import { StandardSchemaV1, StandardSchemaV1 as StandardSchemaV1$1 } from "@standard-schema/spec";
|
|
4
|
+
|
|
5
|
+
//#region src/integrations/standard-schema/index.d.ts
|
|
6
|
+
/** Options captured by a Standard Schema adapter at construction time. */
|
|
7
|
+
type StandardSchemaAdapterOptions = PayloadValidationContext;
|
|
8
|
+
/** Runtime-only Standard Schema surface for a dynamic SchemaIR declaration. */
|
|
9
|
+
type SchemaIRStandardSchema = {
|
|
10
|
+
readonly '~standard': Omit<StandardSchemaV1$1.Props, 'types'>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Expose a serializable SchemaIR declaration through the Standard Schema
|
|
14
|
+
* runtime protocol. Validation is synchronous; load external refs and policy
|
|
15
|
+
* data before constructing the adapter.
|
|
16
|
+
*
|
|
17
|
+
* The adapter never transforms a payload and intentionally does not expose
|
|
18
|
+
* `~standard.types` because the declaration is runtime data.
|
|
19
|
+
*/
|
|
20
|
+
declare function schemaIRToStandardSchema(schema: SchemaNode, options?: StandardSchemaAdapterOptions): SchemaIRStandardSchema;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { SchemaIRStandardSchema, StandardSchemaAdapterOptions, type StandardSchemaV1, schemaIRToStandardSchema };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { validatePayload } from "../../payload/index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/integrations/standard-schema/index.ts
|
|
4
|
+
function toStandardIssue(issue) {
|
|
5
|
+
const path = issue.path.length > 0 ? [...issue.path] : void 0;
|
|
6
|
+
return path === void 0 ? { message: issue.message } : {
|
|
7
|
+
message: issue.message,
|
|
8
|
+
path
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Expose a serializable SchemaIR declaration through the Standard Schema
|
|
13
|
+
* runtime protocol. Validation is synchronous; load external refs and policy
|
|
14
|
+
* data before constructing the adapter.
|
|
15
|
+
*
|
|
16
|
+
* The adapter never transforms a payload and intentionally does not expose
|
|
17
|
+
* `~standard.types` because the declaration is runtime data.
|
|
18
|
+
*/
|
|
19
|
+
function schemaIRToStandardSchema(schema, options = {}) {
|
|
20
|
+
return { "~standard": {
|
|
21
|
+
version: 1,
|
|
22
|
+
vendor: "schemair",
|
|
23
|
+
validate: (value) => {
|
|
24
|
+
const result = validatePayload(value, schema, options);
|
|
25
|
+
if (result.status === "accepted") return { value };
|
|
26
|
+
return { issues: result.issues.map(toStandardIssue) };
|
|
27
|
+
}
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { schemaIRToStandardSchema };
|
package/dist/payload/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const require_definition = require('../definition-
|
|
1
|
+
const require_definition = require('../definition-BLLBpLKv.cjs');
|
|
2
|
+
const require_schema_ref_key = require('../schema-ref-key-BHVFVfj1.cjs');
|
|
2
3
|
|
|
3
4
|
//#region src/payload/validator.ts
|
|
4
5
|
function hasOwn(value, key) {
|
|
@@ -38,22 +39,19 @@ function hasAccessorOrSymbol(value) {
|
|
|
38
39
|
function isPromiseLike(value) {
|
|
39
40
|
return (typeof value === "object" && value !== null || typeof value === "function") && typeof value.then === "function";
|
|
40
41
|
}
|
|
41
|
-
function chain(previous, next) {
|
|
42
|
-
return isPromiseLike(previous) ? Promise.resolve(previous).then(() => next()) : next();
|
|
43
|
-
}
|
|
44
42
|
var PayloadValidator = class {
|
|
45
43
|
issues = [];
|
|
46
44
|
activePayload = /* @__PURE__ */ new WeakSet();
|
|
47
45
|
activeRefs = /* @__PURE__ */ new Set();
|
|
46
|
+
refIndex;
|
|
48
47
|
rootSchema;
|
|
49
|
-
constructor(context, rootSchema
|
|
48
|
+
constructor(context, rootSchema) {
|
|
50
49
|
this.context = context;
|
|
51
|
-
this.allowAsync = allowAsync;
|
|
52
50
|
this.rootSchema = rootSchema;
|
|
51
|
+
this.refIndex = require_schema_ref_key.indexSchemaRefs(context.refs);
|
|
53
52
|
}
|
|
54
53
|
run(value) {
|
|
55
|
-
|
|
56
|
-
if (isPromiseLike(validation)) return Promise.resolve(validation).then(() => this.result());
|
|
54
|
+
this.validateNode(value, this.rootSchema, [], this.rootSchema);
|
|
57
55
|
return this.result();
|
|
58
56
|
}
|
|
59
57
|
result() {
|
|
@@ -93,35 +91,29 @@ var PayloadValidator = class {
|
|
|
93
91
|
}
|
|
94
92
|
validateNode(value, schema, path, root) {
|
|
95
93
|
switch (schema.kind) {
|
|
96
|
-
case "primitive":
|
|
94
|
+
case "primitive":
|
|
97
95
|
if (!this.matchesPrimitive(value, schema.name)) {
|
|
98
96
|
this.reject(path, "type", `expected ${schema.name}`);
|
|
99
97
|
return;
|
|
100
98
|
}
|
|
101
|
-
|
|
102
|
-
if ((schema.name === "string" || schema.name === "number") && schema.
|
|
103
|
-
|
|
104
|
-
return pending;
|
|
105
|
-
}
|
|
99
|
+
if ((schema.name === "string" || schema.name === "number") && schema.semanticPath) this.runSemantic(schema.semanticPath, value, schema, path, root);
|
|
100
|
+
if ((schema.name === "string" || schema.name === "number") && schema.constraints) this.runConstraints(schema.constraints, value, schema, path, root);
|
|
101
|
+
return;
|
|
106
102
|
case "literal":
|
|
107
103
|
if (!isLiteral(value) || !equalLiteral(value, schema.literal)) this.reject(path, "literal", "value does not match literal");
|
|
108
104
|
return;
|
|
109
105
|
case "record": return this.validateRecord(value, schema, path, root);
|
|
110
|
-
case "array":
|
|
106
|
+
case "array":
|
|
111
107
|
if (!isDenseArray(value)) {
|
|
112
108
|
this.reject(path, "array", "expected a dense array");
|
|
113
109
|
return;
|
|
114
110
|
}
|
|
115
111
|
if (!this.enterPayload(value, path)) return;
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
return pending;
|
|
123
|
-
}
|
|
124
|
-
case "tuple": {
|
|
112
|
+
for (let index = 0; index < value.length; index += 1) this.validateNode(value[index], schema.elementSchema, [...path, index], root);
|
|
113
|
+
this.leavePayload(value);
|
|
114
|
+
if (schema.constraints) this.runConstraints(schema.constraints, value, schema, path, root);
|
|
115
|
+
return;
|
|
116
|
+
case "tuple":
|
|
125
117
|
if (!isDenseArray(value)) {
|
|
126
118
|
this.reject(path, "tuple", "expected a dense array");
|
|
127
119
|
return;
|
|
@@ -131,15 +123,12 @@ var PayloadValidator = class {
|
|
|
131
123
|
return;
|
|
132
124
|
}
|
|
133
125
|
if (!this.enterPayload(value, path)) return;
|
|
134
|
-
let pending = void 0;
|
|
135
126
|
for (let index = 0; index < schema.elementSchemas.length; index += 1) {
|
|
136
127
|
const elementSchema = schema.elementSchemas[index];
|
|
137
|
-
if (elementSchema)
|
|
128
|
+
if (elementSchema) this.validateNode(value[index], elementSchema, [...path, index], root);
|
|
138
129
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
}
|
|
130
|
+
this.leavePayload(value);
|
|
131
|
+
return;
|
|
143
132
|
case "literalUnion":
|
|
144
133
|
if (!isLiteral(value) || !schema.values.some((candidate) => equalLiteral(candidate, value))) this.reject(path, "literal-union", "value is not one of the allowed literals");
|
|
145
134
|
return;
|
|
@@ -165,26 +154,23 @@ var PayloadValidator = class {
|
|
|
165
154
|
}
|
|
166
155
|
if (!this.enterPayload(value, path)) return;
|
|
167
156
|
const fields = new Map(schema.fields.map((field) => [field.key, field]));
|
|
168
|
-
let pending = void 0;
|
|
169
157
|
for (const field of schema.fields) {
|
|
170
158
|
if (!hasOwn(value, field.key)) {
|
|
171
159
|
if (field.required === true) this.reject([...path, field.key], "required", "required field is missing");
|
|
172
160
|
continue;
|
|
173
161
|
}
|
|
174
|
-
|
|
162
|
+
this.validateNode(value[field.key], field.schema, [...path, field.key], root);
|
|
175
163
|
}
|
|
176
164
|
for (const key of Object.keys(value)) {
|
|
177
165
|
if (fields.has(key)) continue;
|
|
178
166
|
if (!hasOwn(schema, "additionalFields")) this.reject([...path, key], "additional-field", "unknown field is not allowed");
|
|
179
167
|
else if (schema.additionalFields !== true) {
|
|
180
168
|
const additionalSchema = schema.additionalFields;
|
|
181
|
-
if (additionalSchema)
|
|
169
|
+
if (additionalSchema) this.validateNode(value[key], additionalSchema, [...path, key], root);
|
|
182
170
|
} else if (!this.isJsonValue(value[key], /* @__PURE__ */ new WeakSet())) this.reject([...path, key], "additional-value", "additional field must be JSON-compatible");
|
|
183
171
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return schema.constraints ? this.runConstraints(schema.constraints, value, schema, path, root) : void 0;
|
|
187
|
-
});
|
|
172
|
+
this.leavePayload(value);
|
|
173
|
+
if (schema.constraints) this.runConstraints(schema.constraints, value, schema, path, root);
|
|
188
174
|
}
|
|
189
175
|
validateTaggedUnion(value, schema, path, root) {
|
|
190
176
|
if (!isPlainRecord(value) || hasAccessorOrSymbol(value)) {
|
|
@@ -207,15 +193,18 @@ var PayloadValidator = class {
|
|
|
207
193
|
return this.validateNode(value, variant.schema, path, root);
|
|
208
194
|
}
|
|
209
195
|
validateRef(value, schema, path, root) {
|
|
210
|
-
|
|
211
|
-
this.unsupported(path, "ref.resolver", "refPath requires an explicit resolver");
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const refKey = schema.refPath.join("\0");
|
|
196
|
+
const refKey = require_schema_ref_key.schemaRefKey(schema.refPath);
|
|
215
197
|
if (this.activeRefs.has(refKey)) {
|
|
216
198
|
this.unknown(path, "ref.cycle", "refPath resolution cycle could not be determined");
|
|
217
199
|
return;
|
|
218
200
|
}
|
|
201
|
+
const materialized = this.refIndex.get(refKey);
|
|
202
|
+
if (materialized !== void 0) return this.handleResolution(materialized, value, path, root);
|
|
203
|
+
if (!this.context.resolveRef) {
|
|
204
|
+
if (this.context.refs !== void 0) this.unknown(path, "ref.unresolved", "refPath is not present in the resolved environment");
|
|
205
|
+
else this.unsupported(path, "ref.resolver", "refPath requires an explicit resolver");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
219
208
|
this.activeRefs.add(refKey);
|
|
220
209
|
let resolved;
|
|
221
210
|
try {
|
|
@@ -231,19 +220,15 @@ var PayloadValidator = class {
|
|
|
231
220
|
return;
|
|
232
221
|
}
|
|
233
222
|
if (isPromiseLike(resolved)) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
223
|
+
this.unsupported(path, "ref.async", "synchronous validation requires a synchronous ref resolver");
|
|
224
|
+
this.activeRefs.delete(refKey);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
this.handleResolution(resolved, value, path, root);
|
|
229
|
+
} finally {
|
|
230
|
+
this.activeRefs.delete(refKey);
|
|
242
231
|
}
|
|
243
|
-
const handled = this.handleResolution(resolved, value, path, root);
|
|
244
|
-
if (isPromiseLike(handled)) return Promise.resolve(handled).finally(() => this.activeRefs.delete(refKey));
|
|
245
|
-
this.activeRefs.delete(refKey);
|
|
246
|
-
return handled;
|
|
247
232
|
}
|
|
248
233
|
handleResolution(resolved, value, path, root) {
|
|
249
234
|
if (resolved === void 0) {
|
|
@@ -279,19 +264,17 @@ var PayloadValidator = class {
|
|
|
279
264
|
}), path, "semantic");
|
|
280
265
|
}
|
|
281
266
|
runConstraints(constraints, value, schema, path, root) {
|
|
282
|
-
let pending = void 0;
|
|
283
267
|
for (const constraint of constraints) {
|
|
284
268
|
if (!this.context.constraintProvider) {
|
|
285
269
|
this.unsupported(path, "constraint.provider", `constraint path ${constraint.constraintPath.join("/")} has no provider`);
|
|
286
270
|
continue;
|
|
287
271
|
}
|
|
288
|
-
|
|
272
|
+
this.applyProvider(() => this.context.constraintProvider(constraint.constraintPath, constraint.args, value, {
|
|
289
273
|
path,
|
|
290
274
|
schema,
|
|
291
275
|
rootSchema: root
|
|
292
|
-
}), path, "constraint")
|
|
276
|
+
}), path, "constraint");
|
|
293
277
|
}
|
|
294
|
-
return pending;
|
|
295
278
|
}
|
|
296
279
|
applyProvider(call, path, kind) {
|
|
297
280
|
let result;
|
|
@@ -302,13 +285,8 @@ var PayloadValidator = class {
|
|
|
302
285
|
return;
|
|
303
286
|
}
|
|
304
287
|
if (isPromiseLike(result)) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
return Promise.resolve(result).then((decision) => this.applyDecision(decision, path, kind)).catch((error) => {
|
|
310
|
-
this.unknown(path, `${kind}.error`, `${kind} provider failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
311
|
-
});
|
|
288
|
+
this.unsupported(path, `${kind}.async`, `synchronous validation requires a synchronous ${kind} provider`);
|
|
289
|
+
return;
|
|
312
290
|
}
|
|
313
291
|
return this.applyDecision(result, path, kind);
|
|
314
292
|
}
|
|
@@ -368,23 +346,8 @@ function validateDefinition(schema) {
|
|
|
368
346
|
function validatePayload(value, schema, context = {}) {
|
|
369
347
|
const validated = validateDefinition(schema);
|
|
370
348
|
if ("status" in validated) return validated;
|
|
371
|
-
|
|
372
|
-
if (isPromiseLike(result)) return {
|
|
373
|
-
status: "unknown",
|
|
374
|
-
issues: [{
|
|
375
|
-
code: "payload.unknown.async",
|
|
376
|
-
path: [],
|
|
377
|
-
message: "synchronous payload validation produced an asynchronous result"
|
|
378
|
-
}]
|
|
379
|
-
};
|
|
380
|
-
return result;
|
|
381
|
-
}
|
|
382
|
-
async function validatePayloadAsync(value, schema, context = {}) {
|
|
383
|
-
const validated = validateDefinition(schema);
|
|
384
|
-
if ("status" in validated) return validated;
|
|
385
|
-
return Promise.resolve(new PayloadValidator(context, validated, true).run(value));
|
|
349
|
+
return new PayloadValidator(context, validated).run(value);
|
|
386
350
|
}
|
|
387
351
|
|
|
388
352
|
//#endregion
|
|
389
|
-
exports.validatePayload = validatePayload;
|
|
390
|
-
exports.validatePayloadAsync = validatePayloadAsync;
|
|
353
|
+
exports.validatePayload = validatePayload;
|