@dynz/to-json-schema 0.0.1 → 0.1.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/dist/index.cjs +109 -39
- package/dist/index.d.cts +34 -2
- package/dist/index.d.mts +34 -2
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +109 -39
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -114,6 +114,12 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
114
114
|
else reportIssue(context, `"equals" rule with a non-static value cannot be converted to JSON Schema.`);
|
|
115
115
|
break;
|
|
116
116
|
}
|
|
117
|
+
case "not_equals": {
|
|
118
|
+
const notEquals = resolveStatic(rule.notEquals);
|
|
119
|
+
if (notEquals.ok) setKeyword(jsonSchema, "not", { const: notEquals.value });
|
|
120
|
+
else reportIssue(context, `"not_equals" rule with a non-static value cannot be converted to JSON Schema.`);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
117
123
|
case "includes": {
|
|
118
124
|
const includes = resolveStatic(rule.includes);
|
|
119
125
|
if (!includes.ok) {
|
|
@@ -174,6 +180,8 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
174
180
|
case "max_date":
|
|
175
181
|
case "before":
|
|
176
182
|
case "after":
|
|
183
|
+
case "same_calendar":
|
|
184
|
+
case "boundary_day":
|
|
177
185
|
reportIssue(context, `"${rule.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);
|
|
178
186
|
break;
|
|
179
187
|
case "min_size":
|
|
@@ -186,6 +194,9 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
186
194
|
case "conditional":
|
|
187
195
|
reportIssue(context, `"conditional" rule has no JSON Schema equivalent.`);
|
|
188
196
|
break;
|
|
197
|
+
case "satisfies":
|
|
198
|
+
reportIssue(context, `"satisfies" rule has no JSON Schema equivalent.`);
|
|
199
|
+
break;
|
|
189
200
|
default: reportIssue(context, `Unknown rule cannot be converted to JSON Schema.`);
|
|
190
201
|
}
|
|
191
202
|
}
|
|
@@ -198,7 +209,8 @@ function applyRules(jsonSchema, rules, schemaType, context) {
|
|
|
198
209
|
function isMandatory(schema) {
|
|
199
210
|
const isIncluded = schema.included === void 0 || schema.included === true;
|
|
200
211
|
const isRequired = schema.required === void 0 || schema.required === true;
|
|
201
|
-
|
|
212
|
+
const hasDefault = "default" in schema && schema.default !== void 0;
|
|
213
|
+
return isIncluded && isRequired && !hasDefault;
|
|
202
214
|
}
|
|
203
215
|
function jsonTypeOf(value) {
|
|
204
216
|
return typeof value;
|
|
@@ -211,47 +223,101 @@ function applyEnumValues(jsonSchema, values) {
|
|
|
211
223
|
}
|
|
212
224
|
function collectOptionValues(options, context) {
|
|
213
225
|
const values = [];
|
|
214
|
-
const
|
|
226
|
+
for (const option of options) {
|
|
215
227
|
if (typeof option !== "object") {
|
|
216
228
|
values.push(option);
|
|
217
|
-
|
|
229
|
+
continue;
|
|
218
230
|
}
|
|
219
|
-
if (option.enabled === false)
|
|
231
|
+
if (option.enabled === false) continue;
|
|
220
232
|
if (typeof option.enabled === "object") reportIssue(context, `An "options" entry has a predicate-based "enabled" flag; whether it's enabled can't be determined statically, so it was included in the JSON Schema "enum" anyway.`);
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
for (const option of options) visit(option);
|
|
233
|
+
values.push(option.value);
|
|
234
|
+
}
|
|
224
235
|
return values;
|
|
225
236
|
}
|
|
226
237
|
function isSchema(value) {
|
|
227
238
|
return typeof value === "object" && value !== null && "type" in value;
|
|
228
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Wraps a property's schema so it also accepts `null` — strict mode's only
|
|
242
|
+
* way to express "may be absent", since every property must be listed in
|
|
243
|
+
* `required`.
|
|
244
|
+
*/
|
|
245
|
+
function withNullable(jsonSchema) {
|
|
246
|
+
return { anyOf: [jsonSchema, { type: "null" }] };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* A `const` node, paired with its inferred `type` under `strict` (strict
|
|
250
|
+
* mode requires `type` everywhere; a bare `const` has none otherwise).
|
|
251
|
+
*/
|
|
252
|
+
function constNode(value, strict) {
|
|
253
|
+
return strict ? {
|
|
254
|
+
const: value,
|
|
255
|
+
type: value === null ? "null" : typeof value
|
|
256
|
+
} : { const: value };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Converts a set of object fields into `properties`/`required`, shared by
|
|
260
|
+
* the OBJECT case and each `discriminatedUnion()` member. Under `strict`,
|
|
261
|
+
* every non-omitted field is listed in `required` — one that isn't
|
|
262
|
+
* otherwise mandatory has its schema widened to also accept `null`.
|
|
263
|
+
*/
|
|
264
|
+
function convertObjectFields(fields, context) {
|
|
265
|
+
const properties = {};
|
|
266
|
+
const required = [];
|
|
267
|
+
for (const [key, fieldSchema] of Object.entries(fields)) {
|
|
268
|
+
if (shouldOmitField(fieldSchema, context)) continue;
|
|
269
|
+
const propertySchema = convertSchema(fieldSchema, context);
|
|
270
|
+
const mandatory = isMandatory(fieldSchema);
|
|
271
|
+
properties[key] = context.strict && !mandatory ? withNullable(propertySchema) : propertySchema;
|
|
272
|
+
if (mandatory || context.strict) required.push(key);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
properties,
|
|
276
|
+
required
|
|
277
|
+
};
|
|
278
|
+
}
|
|
229
279
|
function shouldOmitField(schema, context) {
|
|
230
280
|
if (schema.included === false) return true;
|
|
231
281
|
return context.mode === "input" && schema.type === dynz.SchemaType.EXPRESSION;
|
|
232
282
|
}
|
|
233
|
-
function applyPrivacyWrapper(schema, innerSchema) {
|
|
283
|
+
function applyPrivacyWrapper(schema, innerSchema, context) {
|
|
234
284
|
if (schema.private !== true) return innerSchema;
|
|
235
|
-
|
|
285
|
+
const plain = {
|
|
236
286
|
type: "object",
|
|
237
287
|
properties: {
|
|
238
|
-
state:
|
|
288
|
+
state: constNode("plain", context.strict),
|
|
239
289
|
value: innerSchema
|
|
240
290
|
},
|
|
241
|
-
required: ["state"]
|
|
242
|
-
}
|
|
291
|
+
required: context.strict ? ["state", "value"] : ["state"]
|
|
292
|
+
};
|
|
293
|
+
const masked = {
|
|
243
294
|
type: "object",
|
|
244
295
|
properties: {
|
|
245
|
-
state:
|
|
296
|
+
state: constNode("masked", context.strict),
|
|
246
297
|
value: { type: "string" }
|
|
247
298
|
},
|
|
248
299
|
required: ["state", "value"]
|
|
249
|
-
}
|
|
300
|
+
};
|
|
301
|
+
if (context.strict) {
|
|
302
|
+
plain.additionalProperties = false;
|
|
303
|
+
masked.additionalProperties = false;
|
|
304
|
+
}
|
|
305
|
+
return { [context.unionKeyword]: [plain, masked] };
|
|
250
306
|
}
|
|
251
307
|
function applyDefault(schema, jsonSchema) {
|
|
252
308
|
if (schema.default === void 0) return;
|
|
253
309
|
jsonSchema.default = schema.default instanceof Date ? schema.default.toISOString() : schema.default;
|
|
254
310
|
}
|
|
311
|
+
function applyMeta(schema, jsonSchema) {
|
|
312
|
+
const meta = schema.meta;
|
|
313
|
+
if (!meta) return;
|
|
314
|
+
const { id, title, description, deprecated, ...rest } = meta;
|
|
315
|
+
if (id !== void 0) jsonSchema.$id = id;
|
|
316
|
+
if (title !== void 0) jsonSchema.title = title;
|
|
317
|
+
if (description !== void 0) jsonSchema.description = description;
|
|
318
|
+
if (deprecated !== void 0) jsonSchema.deprecated = deprecated;
|
|
319
|
+
Object.assign(jsonSchema, rest);
|
|
320
|
+
}
|
|
255
321
|
/**
|
|
256
322
|
* Converts a dynz schema to a JSON Schema (2020-12) document. Rule values
|
|
257
323
|
* and schema kinds without a JSON Schema equivalent are handled per
|
|
@@ -260,7 +326,8 @@ function applyDefault(schema, jsonSchema) {
|
|
|
260
326
|
function convertSchema(schema, context) {
|
|
261
327
|
const jsonSchema = convertSchemaKind(schema, context);
|
|
262
328
|
applyDefault(schema, jsonSchema);
|
|
263
|
-
|
|
329
|
+
applyMeta(schema, jsonSchema);
|
|
330
|
+
return applyPrivacyWrapper(schema, jsonSchema, context);
|
|
264
331
|
}
|
|
265
332
|
function convertSchemaKind(schema, context) {
|
|
266
333
|
switch (schema.type) {
|
|
@@ -287,7 +354,7 @@ function convertSchemaKind(schema, context) {
|
|
|
287
354
|
applyRules(jsonSchema, schema.rules, schema.type, context);
|
|
288
355
|
return jsonSchema;
|
|
289
356
|
}
|
|
290
|
-
case dynz.SchemaType.LITERAL: return
|
|
357
|
+
case dynz.SchemaType.LITERAL: return constNode(schema.value, context.strict);
|
|
291
358
|
case dynz.SchemaType.ENUM: {
|
|
292
359
|
const jsonSchema = {};
|
|
293
360
|
applyEnumValues(jsonSchema, Object.values(schema.enum));
|
|
@@ -315,35 +382,33 @@ function convertSchemaKind(schema, context) {
|
|
|
315
382
|
return jsonSchema;
|
|
316
383
|
}
|
|
317
384
|
case dynz.SchemaType.OBJECT: {
|
|
318
|
-
const properties =
|
|
319
|
-
const required = [];
|
|
320
|
-
for (const [key, fieldSchema] of Object.entries(schema.fields)) {
|
|
321
|
-
if (shouldOmitField(fieldSchema, context)) continue;
|
|
322
|
-
properties[key] = convertSchema(fieldSchema, context);
|
|
323
|
-
if (isMandatory(fieldSchema)) required.push(key);
|
|
324
|
-
}
|
|
385
|
+
const { properties, required } = convertObjectFields(schema.fields, context);
|
|
325
386
|
const jsonSchema = {
|
|
326
387
|
type: "object",
|
|
327
388
|
properties
|
|
328
389
|
};
|
|
329
390
|
if (required.length > 0) jsonSchema.required = required;
|
|
391
|
+
if (context.strict) jsonSchema.additionalProperties = false;
|
|
330
392
|
applyRules(jsonSchema, schema.rules, schema.type, context);
|
|
331
393
|
return jsonSchema;
|
|
332
394
|
}
|
|
333
|
-
case dynz.SchemaType.DISCRIMINATED_UNION:
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
properties[key] =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
395
|
+
case dynz.SchemaType.DISCRIMINATED_UNION: {
|
|
396
|
+
const members = schema.schemas.map((member) => {
|
|
397
|
+
const memberFields = {};
|
|
398
|
+
for (const [key, value] of Object.entries(member)) if (key !== schema.key && isSchema(value)) memberFields[key] = value;
|
|
399
|
+
const { properties, required } = convertObjectFields(memberFields, context);
|
|
400
|
+
properties[schema.key] = constNode(member[schema.key], context.strict);
|
|
401
|
+
required.unshift(schema.key);
|
|
402
|
+
const jsonSchema = {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties,
|
|
405
|
+
required
|
|
406
|
+
};
|
|
407
|
+
if (context.strict) jsonSchema.additionalProperties = false;
|
|
408
|
+
return jsonSchema;
|
|
409
|
+
});
|
|
410
|
+
return { [context.unionKeyword]: members };
|
|
411
|
+
}
|
|
347
412
|
default:
|
|
348
413
|
reportIssue(context, `Unknown schema type cannot be converted to JSON Schema.`);
|
|
349
414
|
return {};
|
|
@@ -359,14 +424,19 @@ function convertSchemaKind(schema, context) {
|
|
|
359
424
|
* transformers) and schema kinds without a JSON Schema equivalent are
|
|
360
425
|
* handled according to `config.errorMode` (default `"warn"`). Whether
|
|
361
426
|
* `expression` fields are included is controlled by `config.mode`
|
|
362
|
-
* (default `"input"`, which omits them).
|
|
427
|
+
* (default `"input"`, which omits them). `config.strict` (default `true`)
|
|
428
|
+
* produces output compatible with LLM structured-output "strict" modes,
|
|
429
|
+
* and `config.unionKeyword` (default `"oneOf"`) controls whether schema-
|
|
430
|
+
* selection unions use `oneOf` or `anyOf`.
|
|
363
431
|
*/
|
|
364
432
|
function toStandardJsonSchema(schema, config = {}) {
|
|
365
433
|
return {
|
|
366
434
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
367
435
|
...convertSchema(schema, {
|
|
368
436
|
errorMode: config.errorMode ?? "warn",
|
|
369
|
-
mode: config.mode ?? "input"
|
|
437
|
+
mode: config.mode ?? "input",
|
|
438
|
+
strict: config.strict ?? true,
|
|
439
|
+
unionKeyword: config.unionKeyword ?? "oneOf"
|
|
370
440
|
})
|
|
371
441
|
};
|
|
372
442
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -8,6 +8,10 @@ import { Schema } from "dynz";
|
|
|
8
8
|
*/
|
|
9
9
|
interface JsonSchema {
|
|
10
10
|
$schema?: string;
|
|
11
|
+
$id?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
deprecated?: boolean;
|
|
11
15
|
type?: string | string[];
|
|
12
16
|
properties?: Record<string, JsonSchema>;
|
|
13
17
|
required?: string[];
|
|
@@ -46,6 +50,11 @@ type ErrorMode = "throw" | "warn" | "ignore";
|
|
|
46
50
|
* `"output"` mode they're included (using their best-effort mapping).
|
|
47
51
|
*/
|
|
48
52
|
type ConversionMode = "input" | "output";
|
|
53
|
+
/**
|
|
54
|
+
* The keyword used for a schema-selection union — `discriminatedUnion()`
|
|
55
|
+
* members, and the plain/masked wrapper for `.setPrivate(true)` fields.
|
|
56
|
+
*/
|
|
57
|
+
type UnionKeyword = "oneOf" | "anyOf";
|
|
49
58
|
interface ConversionConfig {
|
|
50
59
|
/**
|
|
51
60
|
* Policy for handling unsupported/unresolvable rule values and schema
|
|
@@ -56,6 +65,26 @@ interface ConversionConfig {
|
|
|
56
65
|
* Whether to convert for input or output data. Defaults to `"input"`.
|
|
57
66
|
*/
|
|
58
67
|
mode?: ConversionMode;
|
|
68
|
+
/**
|
|
69
|
+
* Produces strict-mode-compatible output for LLM structured outputs
|
|
70
|
+
* (OpenAI `strict: true`, Anthropic's native structured output): every
|
|
71
|
+
* object node gets `additionalProperties: false` and a complete
|
|
72
|
+
* `required` list — a property that isn't otherwise mandatory has its
|
|
73
|
+
* schema widened to also accept `null` (via `anyOf`), since strict mode
|
|
74
|
+
* has no other way to express "may be absent". `literal()` fields (and
|
|
75
|
+
* the discriminator key of a `discriminatedUnion()`) also get an
|
|
76
|
+
* inferred `type`, which strict mode requires everywhere. Defaults to
|
|
77
|
+
* `true`.
|
|
78
|
+
*/
|
|
79
|
+
strict?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* The keyword used for every schema-selection union this package emits
|
|
82
|
+
* — `discriminatedUnion()` members, and the plain/masked wrapper for
|
|
83
|
+
* `.setPrivate(true)` fields. Some strict-mode consumers (e.g.
|
|
84
|
+
* Anthropic's structured output) reject `oneOf` outright and require
|
|
85
|
+
* `anyOf`. Defaults to `"oneOf"`.
|
|
86
|
+
*/
|
|
87
|
+
unionKeyword?: UnionKeyword;
|
|
59
88
|
}
|
|
60
89
|
//#endregion
|
|
61
90
|
//#region src/to-standard-json-schema.d.ts
|
|
@@ -66,8 +95,11 @@ interface ConversionConfig {
|
|
|
66
95
|
* transformers) and schema kinds without a JSON Schema equivalent are
|
|
67
96
|
* handled according to `config.errorMode` (default `"warn"`). Whether
|
|
68
97
|
* `expression` fields are included is controlled by `config.mode`
|
|
69
|
-
* (default `"input"`, which omits them).
|
|
98
|
+
* (default `"input"`, which omits them). `config.strict` (default `true`)
|
|
99
|
+
* produces output compatible with LLM structured-output "strict" modes,
|
|
100
|
+
* and `config.unionKeyword` (default `"oneOf"`) controls whether schema-
|
|
101
|
+
* selection unions use `oneOf` or `anyOf`.
|
|
70
102
|
*/
|
|
71
103
|
declare function toStandardJsonSchema<T extends Schema>(schema: T, config?: ConversionConfig): JsonSchema;
|
|
72
104
|
//#endregion
|
|
73
|
-
export { type ConversionConfig, type ErrorMode, type JsonSchema, toStandardJsonSchema };
|
|
105
|
+
export { type ConversionConfig, type ErrorMode, type JsonSchema, type UnionKeyword, toStandardJsonSchema };
|
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,10 @@ import { Schema } from "dynz";
|
|
|
8
8
|
*/
|
|
9
9
|
interface JsonSchema {
|
|
10
10
|
$schema?: string;
|
|
11
|
+
$id?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
deprecated?: boolean;
|
|
11
15
|
type?: string | string[];
|
|
12
16
|
properties?: Record<string, JsonSchema>;
|
|
13
17
|
required?: string[];
|
|
@@ -46,6 +50,11 @@ type ErrorMode = "throw" | "warn" | "ignore";
|
|
|
46
50
|
* `"output"` mode they're included (using their best-effort mapping).
|
|
47
51
|
*/
|
|
48
52
|
type ConversionMode = "input" | "output";
|
|
53
|
+
/**
|
|
54
|
+
* The keyword used for a schema-selection union — `discriminatedUnion()`
|
|
55
|
+
* members, and the plain/masked wrapper for `.setPrivate(true)` fields.
|
|
56
|
+
*/
|
|
57
|
+
type UnionKeyword = "oneOf" | "anyOf";
|
|
49
58
|
interface ConversionConfig {
|
|
50
59
|
/**
|
|
51
60
|
* Policy for handling unsupported/unresolvable rule values and schema
|
|
@@ -56,6 +65,26 @@ interface ConversionConfig {
|
|
|
56
65
|
* Whether to convert for input or output data. Defaults to `"input"`.
|
|
57
66
|
*/
|
|
58
67
|
mode?: ConversionMode;
|
|
68
|
+
/**
|
|
69
|
+
* Produces strict-mode-compatible output for LLM structured outputs
|
|
70
|
+
* (OpenAI `strict: true`, Anthropic's native structured output): every
|
|
71
|
+
* object node gets `additionalProperties: false` and a complete
|
|
72
|
+
* `required` list — a property that isn't otherwise mandatory has its
|
|
73
|
+
* schema widened to also accept `null` (via `anyOf`), since strict mode
|
|
74
|
+
* has no other way to express "may be absent". `literal()` fields (and
|
|
75
|
+
* the discriminator key of a `discriminatedUnion()`) also get an
|
|
76
|
+
* inferred `type`, which strict mode requires everywhere. Defaults to
|
|
77
|
+
* `true`.
|
|
78
|
+
*/
|
|
79
|
+
strict?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* The keyword used for every schema-selection union this package emits
|
|
82
|
+
* — `discriminatedUnion()` members, and the plain/masked wrapper for
|
|
83
|
+
* `.setPrivate(true)` fields. Some strict-mode consumers (e.g.
|
|
84
|
+
* Anthropic's structured output) reject `oneOf` outright and require
|
|
85
|
+
* `anyOf`. Defaults to `"oneOf"`.
|
|
86
|
+
*/
|
|
87
|
+
unionKeyword?: UnionKeyword;
|
|
59
88
|
}
|
|
60
89
|
//#endregion
|
|
61
90
|
//#region src/to-standard-json-schema.d.ts
|
|
@@ -66,8 +95,11 @@ interface ConversionConfig {
|
|
|
66
95
|
* transformers) and schema kinds without a JSON Schema equivalent are
|
|
67
96
|
* handled according to `config.errorMode` (default `"warn"`). Whether
|
|
68
97
|
* `expression` fields are included is controlled by `config.mode`
|
|
69
|
-
* (default `"input"`, which omits them).
|
|
98
|
+
* (default `"input"`, which omits them). `config.strict` (default `true`)
|
|
99
|
+
* produces output compatible with LLM structured-output "strict" modes,
|
|
100
|
+
* and `config.unionKeyword` (default `"oneOf"`) controls whether schema-
|
|
101
|
+
* selection unions use `oneOf` or `anyOf`.
|
|
70
102
|
*/
|
|
71
103
|
declare function toStandardJsonSchema<T extends Schema>(schema: T, config?: ConversionConfig): JsonSchema;
|
|
72
104
|
//#endregion
|
|
73
|
-
export { type ConversionConfig, type ErrorMode, type JsonSchema, toStandardJsonSchema };
|
|
105
|
+
export { type ConversionConfig, type ErrorMode, type JsonSchema, type UnionKeyword, toStandardJsonSchema };
|
package/dist/index.min.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e=require(`dynz`);function t(e,t){if(e.errorMode===`throw`)throw Error(t);e.errorMode===`warn`&&console.warn(`[@dynz/to-json-schema] ${t}`)}function n(e){return e!==void 0&&typeof e==`object`&&`type`in e&&e.type===`st`?{ok:!0,value:e.value}:{ok:!1}}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(e,t,n){if(e[t]===void 0){e[t]=n;return}e.allOf=[...e.allOf??[],{[t]:n}]}function a(e,a,o,s){switch(a.type){case`min`:{let r=n(a.min);r.ok?i(e,`minimum`,r.value):t(s,`"min" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max`:{let r=n(a.max);r.ok?i(e,`maximum`,r.value):t(s,`"max" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_length`:{let r=n(a.min);r.ok?o===`array`?i(e,`minItems`,r.value):i(e,`minLength`,r.value):t(s,`"min_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_length`:{let r=n(a.max);r.ok?o===`array`?i(e,`maxItems`,r.value):i(e,`maxLength`,r.value):t(s,`"max_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_entries`:{let r=n(a.min);r.ok?i(e,`minProperties`,r.value):t(s,`"min_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_entries`:{let r=n(a.max);r.ok?i(e,`maxProperties`,r.value):t(s,`"max_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_precision`:{let r=n(a.maxPrecision);r.ok?i(e,`multipleOf`,1/10**r.value):t(s,`"max_precision" rule with a non-static value cannot be converted to JSON Schema.`);break}case`regex`:i(e,`pattern`,a.regex),a.flags&&t(s,`"regex" rule flags ("${a.flags}") cannot be represented in JSON Schema's "pattern" keyword and were ignored.`);break;case`email`:i(e,`format`,`email`);break;case`is_numeric`:i(e,`pattern`,`^[+-]?\\d+(\\.\\d+)?$`);break;case`equals`:{let r=n(a.equals);r.ok?i(e,`const`,r.value):t(s,`"equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`includes`:{let c=n(a.includes);if(!c.ok){t(s,`"includes" rule with a non-static value cannot be converted to JSON Schema.`);break}o===`array`?i(e,`contains`,{const:c.value}):i(e,`pattern`,r(String(c.value)));break}case`not_includes`:{let c=n(a.notIncludes);if(!c.ok){t(s,`"not_includes" rule with a non-static value cannot be converted to JSON Schema.`);break}i(e,`not`,o===`array`?{contains:{const:c.value}}:{pattern:r(String(c.value))});break}case`one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`enum`,r);break}case`not_one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"not_one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`not`,{enum:r});break}case`mime_type`:{let r=n(a.mimeType);if(!r.ok){t(s,`"mime_type" rule with a non-static value cannot be converted to JSON Schema.`);break}typeof r.value==`string`?i(e,`contentMediaType`,r.value):Array.isArray(r.value)&&r.value.length===1?i(e,`contentMediaType`,r.value[0]):t(s,`"mime_type" rule with multiple mime types has no JSON Schema equivalent.`);break}case`min_date`:case`max_date`:case`before`:case`after`:t(s,`"${a.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);break;case`min_size`:case`max_size`:t(s,`"${a.type}" rule has no JSON Schema equivalent (byte-size constraints aren't representable).`);break;case`custom`:t(s,`"custom" rule "${a.name}" has no JSON Schema equivalent.`);break;case`conditional`:t(s,`"conditional" rule has no JSON Schema equivalent.`);break;default:t(s,`Unknown rule cannot be converted to JSON Schema.`)}}function o(e,t,n,r){for(let i of t??[])a(e,i,n,r)}function s(e){let t=e.included===void 0||e.included===!0,n=e.required===void 0||e.required===!0;return t&&n}function c(e){return typeof e}function l(e,t){e.enum=t;let n=Array.from(new Set(t.map(c)));n.length===1?e.type=n[0]:n.length>1&&(e.type=n)}function u(e,n){let r=[]
|
|
1
|
+
let e=require(`dynz`);function t(e,t){if(e.errorMode===`throw`)throw Error(t);e.errorMode===`warn`&&console.warn(`[@dynz/to-json-schema] ${t}`)}function n(e){return e!==void 0&&typeof e==`object`&&`type`in e&&e.type===`st`?{ok:!0,value:e.value}:{ok:!1}}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(e,t,n){if(e[t]===void 0){e[t]=n;return}e.allOf=[...e.allOf??[],{[t]:n}]}function a(e,a,o,s){switch(a.type){case`min`:{let r=n(a.min);r.ok?i(e,`minimum`,r.value):t(s,`"min" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max`:{let r=n(a.max);r.ok?i(e,`maximum`,r.value):t(s,`"max" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_length`:{let r=n(a.min);r.ok?o===`array`?i(e,`minItems`,r.value):i(e,`minLength`,r.value):t(s,`"min_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_length`:{let r=n(a.max);r.ok?o===`array`?i(e,`maxItems`,r.value):i(e,`maxLength`,r.value):t(s,`"max_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_entries`:{let r=n(a.min);r.ok?i(e,`minProperties`,r.value):t(s,`"min_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_entries`:{let r=n(a.max);r.ok?i(e,`maxProperties`,r.value):t(s,`"max_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_precision`:{let r=n(a.maxPrecision);r.ok?i(e,`multipleOf`,1/10**r.value):t(s,`"max_precision" rule with a non-static value cannot be converted to JSON Schema.`);break}case`regex`:i(e,`pattern`,a.regex),a.flags&&t(s,`"regex" rule flags ("${a.flags}") cannot be represented in JSON Schema's "pattern" keyword and were ignored.`);break;case`email`:i(e,`format`,`email`);break;case`is_numeric`:i(e,`pattern`,`^[+-]?\\d+(\\.\\d+)?$`);break;case`equals`:{let r=n(a.equals);r.ok?i(e,`const`,r.value):t(s,`"equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`not_equals`:{let r=n(a.notEquals);r.ok?i(e,`not`,{const:r.value}):t(s,`"not_equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`includes`:{let c=n(a.includes);if(!c.ok){t(s,`"includes" rule with a non-static value cannot be converted to JSON Schema.`);break}o===`array`?i(e,`contains`,{const:c.value}):i(e,`pattern`,r(String(c.value)));break}case`not_includes`:{let c=n(a.notIncludes);if(!c.ok){t(s,`"not_includes" rule with a non-static value cannot be converted to JSON Schema.`);break}i(e,`not`,o===`array`?{contains:{const:c.value}}:{pattern:r(String(c.value))});break}case`one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`enum`,r);break}case`not_one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"not_one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`not`,{enum:r});break}case`mime_type`:{let r=n(a.mimeType);if(!r.ok){t(s,`"mime_type" rule with a non-static value cannot be converted to JSON Schema.`);break}typeof r.value==`string`?i(e,`contentMediaType`,r.value):Array.isArray(r.value)&&r.value.length===1?i(e,`contentMediaType`,r.value[0]):t(s,`"mime_type" rule with multiple mime types has no JSON Schema equivalent.`);break}case`min_date`:case`max_date`:case`before`:case`after`:case`same_calendar`:case`boundary_day`:t(s,`"${a.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);break;case`min_size`:case`max_size`:t(s,`"${a.type}" rule has no JSON Schema equivalent (byte-size constraints aren't representable).`);break;case`custom`:t(s,`"custom" rule "${a.name}" has no JSON Schema equivalent.`);break;case`conditional`:t(s,`"conditional" rule has no JSON Schema equivalent.`);break;case`satisfies`:t(s,`"satisfies" rule has no JSON Schema equivalent.`);break;default:t(s,`Unknown rule cannot be converted to JSON Schema.`)}}function o(e,t,n,r){for(let i of t??[])a(e,i,n,r)}function s(e){let t=e.included===void 0||e.included===!0,n=e.required===void 0||e.required===!0,r=`default`in e&&e.default!==void 0;return t&&n&&!r}function c(e){return typeof e}function l(e,t){e.enum=t;let n=Array.from(new Set(t.map(c)));n.length===1?e.type=n[0]:n.length>1&&(e.type=n)}function u(e,n){let r=[];for(let i of e){if(typeof i!=`object`){r.push(i);continue}i.enabled!==!1&&(typeof i.enabled==`object`&&t(n,`An "options" entry has a predicate-based "enabled" flag; whether it's enabled can't be determined statically, so it was included in the JSON Schema "enum" anyway.`),r.push(i.value))}return r}function d(e){return typeof e==`object`&&!!e&&`type`in e}function f(e){return{anyOf:[e,{type:`null`}]}}function p(e,t){return t?{const:e,type:e===null?`null`:typeof e}:{const:e}}function m(e,t){let n={},r=[];for(let[i,a]of Object.entries(e)){if(h(a,t))continue;let e=y(a,t),o=s(a);n[i]=t.strict&&!o?f(e):e,(o||t.strict)&&r.push(i)}return{properties:n,required:r}}function h(t,n){return t.included===!1?!0:n.mode===`input`&&t.type===e.SchemaType.EXPRESSION}function g(e,t,n){if(e.private!==!0)return t;let r={type:`object`,properties:{state:p(`plain`,n.strict),value:t},required:n.strict?[`state`,`value`]:[`state`]},i={type:`object`,properties:{state:p(`masked`,n.strict),value:{type:`string`}},required:[`state`,`value`]};return n.strict&&(r.additionalProperties=!1,i.additionalProperties=!1),{[n.unionKeyword]:[r,i]}}function _(e,t){e.default!==void 0&&(t.default=e.default instanceof Date?e.default.toISOString():e.default)}function v(e,t){let n=e.meta;if(!n)return;let{id:r,title:i,description:a,deprecated:o,...s}=n;r!==void 0&&(t.$id=r),i!==void 0&&(t.title=i),a!==void 0&&(t.description=a),o!==void 0&&(t.deprecated=o),Object.assign(t,s)}function y(e,t){let n=b(e,t);return _(e,n),v(e,n),g(e,n,t)}function b(n,r){switch(n.type){case e.SchemaType.STRING:{let e={type:`string`};return o(e,n.rules,n.type,r),e}case e.SchemaType.NUMBER:{let e={type:`number`};return o(e,n.rules,n.type,r),e}case e.SchemaType.BOOLEAN:{let e={type:`boolean`};return o(e,n.rules,n.type,r),e}case e.SchemaType.DATE:{let e={type:`string`,format:`date-time`};return o(e,n.rules,n.type,r),e}case e.SchemaType.LITERAL:return p(n.value,r.strict);case e.SchemaType.ENUM:{let e={};return l(e,Object.values(n.enum)),o(e,n.rules,n.type,r),e}case e.SchemaType.OPTIONS:{let e={};return l(e,u(n.options,r)),o(e,n.rules,n.type,r),e}case e.SchemaType.FILE:{let e={type:`string`};return o(e,n.rules,n.type,r),e}case e.SchemaType.EXPRESSION:return{};case e.SchemaType.ARRAY:{let e={type:`array`,items:y(n.schema,r)};return o(e,n.rules,n.type,r),e}case e.SchemaType.OBJECT:{let{properties:e,required:t}=m(n.fields,r),i={type:`object`,properties:e};return t.length>0&&(i.required=t),r.strict&&(i.additionalProperties=!1),o(i,n.rules,n.type,r),i}case e.SchemaType.DISCRIMINATED_UNION:{let e=n.schemas.map(e=>{let t={};for(let[r,i]of Object.entries(e))r!==n.key&&d(i)&&(t[r]=i);let{properties:i,required:a}=m(t,r);i[n.key]=p(e[n.key],r.strict),a.unshift(n.key);let o={type:`object`,properties:i,required:a};return r.strict&&(o.additionalProperties=!1),o});return{[r.unionKeyword]:e}}default:return t(r,`Unknown schema type cannot be converted to JSON Schema.`),{}}}function x(e,t={}){return{$schema:`https://json-schema.org/draft/2020-12/schema`,...y(e,{errorMode:t.errorMode??`warn`,mode:t.mode??`input`,strict:t.strict??!0,unionKeyword:t.unionKeyword??`oneOf`})}}exports.toStandardJsonSchema=x;
|
package/dist/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SchemaType as e}from"dynz";function t(e,t){if(e.errorMode===`throw`)throw Error(t);e.errorMode===`warn`&&console.warn(`[@dynz/to-json-schema] ${t}`)}function n(e){return e!==void 0&&typeof e==`object`&&`type`in e&&e.type===`st`?{ok:!0,value:e.value}:{ok:!1}}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(e,t,n){if(e[t]===void 0){e[t]=n;return}e.allOf=[...e.allOf??[],{[t]:n}]}function a(e,a,o,s){switch(a.type){case`min`:{let r=n(a.min);r.ok?i(e,`minimum`,r.value):t(s,`"min" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max`:{let r=n(a.max);r.ok?i(e,`maximum`,r.value):t(s,`"max" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_length`:{let r=n(a.min);r.ok?o===`array`?i(e,`minItems`,r.value):i(e,`minLength`,r.value):t(s,`"min_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_length`:{let r=n(a.max);r.ok?o===`array`?i(e,`maxItems`,r.value):i(e,`maxLength`,r.value):t(s,`"max_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_entries`:{let r=n(a.min);r.ok?i(e,`minProperties`,r.value):t(s,`"min_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_entries`:{let r=n(a.max);r.ok?i(e,`maxProperties`,r.value):t(s,`"max_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_precision`:{let r=n(a.maxPrecision);r.ok?i(e,`multipleOf`,1/10**r.value):t(s,`"max_precision" rule with a non-static value cannot be converted to JSON Schema.`);break}case`regex`:i(e,`pattern`,a.regex),a.flags&&t(s,`"regex" rule flags ("${a.flags}") cannot be represented in JSON Schema's "pattern" keyword and were ignored.`);break;case`email`:i(e,`format`,`email`);break;case`is_numeric`:i(e,`pattern`,`^[+-]?\\d+(\\.\\d+)?$`);break;case`equals`:{let r=n(a.equals);r.ok?i(e,`const`,r.value):t(s,`"equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`includes`:{let c=n(a.includes);if(!c.ok){t(s,`"includes" rule with a non-static value cannot be converted to JSON Schema.`);break}o===`array`?i(e,`contains`,{const:c.value}):i(e,`pattern`,r(String(c.value)));break}case`not_includes`:{let c=n(a.notIncludes);if(!c.ok){t(s,`"not_includes" rule with a non-static value cannot be converted to JSON Schema.`);break}i(e,`not`,o===`array`?{contains:{const:c.value}}:{pattern:r(String(c.value))});break}case`one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`enum`,r);break}case`not_one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"not_one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`not`,{enum:r});break}case`mime_type`:{let r=n(a.mimeType);if(!r.ok){t(s,`"mime_type" rule with a non-static value cannot be converted to JSON Schema.`);break}typeof r.value==`string`?i(e,`contentMediaType`,r.value):Array.isArray(r.value)&&r.value.length===1?i(e,`contentMediaType`,r.value[0]):t(s,`"mime_type" rule with multiple mime types has no JSON Schema equivalent.`);break}case`min_date`:case`max_date`:case`before`:case`after`:t(s,`"${a.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);break;case`min_size`:case`max_size`:t(s,`"${a.type}" rule has no JSON Schema equivalent (byte-size constraints aren't representable).`);break;case`custom`:t(s,`"custom" rule "${a.name}" has no JSON Schema equivalent.`);break;case`conditional`:t(s,`"conditional" rule has no JSON Schema equivalent.`);break;default:t(s,`Unknown rule cannot be converted to JSON Schema.`)}}function o(e,t,n,r){for(let i of t??[])a(e,i,n,r)}function s(e){let t=e.included===void 0||e.included===!0,n=e.required===void 0||e.required===!0;return t&&n}function c(e){return typeof e}function l(e,t){e.enum=t;let n=Array.from(new Set(t.map(c)));n.length===1?e.type=n[0]:n.length>1&&(e.type=n)}function u(e,n){let r=[]
|
|
1
|
+
import{SchemaType as e}from"dynz";function t(e,t){if(e.errorMode===`throw`)throw Error(t);e.errorMode===`warn`&&console.warn(`[@dynz/to-json-schema] ${t}`)}function n(e){return e!==void 0&&typeof e==`object`&&`type`in e&&e.type===`st`?{ok:!0,value:e.value}:{ok:!1}}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(e,t,n){if(e[t]===void 0){e[t]=n;return}e.allOf=[...e.allOf??[],{[t]:n}]}function a(e,a,o,s){switch(a.type){case`min`:{let r=n(a.min);r.ok?i(e,`minimum`,r.value):t(s,`"min" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max`:{let r=n(a.max);r.ok?i(e,`maximum`,r.value):t(s,`"max" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_length`:{let r=n(a.min);r.ok?o===`array`?i(e,`minItems`,r.value):i(e,`minLength`,r.value):t(s,`"min_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_length`:{let r=n(a.max);r.ok?o===`array`?i(e,`maxItems`,r.value):i(e,`maxLength`,r.value):t(s,`"max_length" rule with a non-static value cannot be converted to JSON Schema.`);break}case`min_entries`:{let r=n(a.min);r.ok?i(e,`minProperties`,r.value):t(s,`"min_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_entries`:{let r=n(a.max);r.ok?i(e,`maxProperties`,r.value):t(s,`"max_entries" rule with a non-static value cannot be converted to JSON Schema.`);break}case`max_precision`:{let r=n(a.maxPrecision);r.ok?i(e,`multipleOf`,1/10**r.value):t(s,`"max_precision" rule with a non-static value cannot be converted to JSON Schema.`);break}case`regex`:i(e,`pattern`,a.regex),a.flags&&t(s,`"regex" rule flags ("${a.flags}") cannot be represented in JSON Schema's "pattern" keyword and were ignored.`);break;case`email`:i(e,`format`,`email`);break;case`is_numeric`:i(e,`pattern`,`^[+-]?\\d+(\\.\\d+)?$`);break;case`equals`:{let r=n(a.equals);r.ok?i(e,`const`,r.value):t(s,`"equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`not_equals`:{let r=n(a.notEquals);r.ok?i(e,`not`,{const:r.value}):t(s,`"not_equals" rule with a non-static value cannot be converted to JSON Schema.`);break}case`includes`:{let c=n(a.includes);if(!c.ok){t(s,`"includes" rule with a non-static value cannot be converted to JSON Schema.`);break}o===`array`?i(e,`contains`,{const:c.value}):i(e,`pattern`,r(String(c.value)));break}case`not_includes`:{let c=n(a.notIncludes);if(!c.ok){t(s,`"not_includes" rule with a non-static value cannot be converted to JSON Schema.`);break}i(e,`not`,o===`array`?{contains:{const:c.value}}:{pattern:r(String(c.value))});break}case`one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`enum`,r);break}case`not_one_of`:{let r=[];for(let e of a.values){let i=n(e);if(!i.ok){t(s,`"not_one_of" rule with a non-static value cannot be converted to JSON Schema.`);return}r.push(i.value)}i(e,`not`,{enum:r});break}case`mime_type`:{let r=n(a.mimeType);if(!r.ok){t(s,`"mime_type" rule with a non-static value cannot be converted to JSON Schema.`);break}typeof r.value==`string`?i(e,`contentMediaType`,r.value):Array.isArray(r.value)&&r.value.length===1?i(e,`contentMediaType`,r.value[0]):t(s,`"mime_type" rule with multiple mime types has no JSON Schema equivalent.`);break}case`min_date`:case`max_date`:case`before`:case`after`:case`same_calendar`:case`boundary_day`:t(s,`"${a.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);break;case`min_size`:case`max_size`:t(s,`"${a.type}" rule has no JSON Schema equivalent (byte-size constraints aren't representable).`);break;case`custom`:t(s,`"custom" rule "${a.name}" has no JSON Schema equivalent.`);break;case`conditional`:t(s,`"conditional" rule has no JSON Schema equivalent.`);break;case`satisfies`:t(s,`"satisfies" rule has no JSON Schema equivalent.`);break;default:t(s,`Unknown rule cannot be converted to JSON Schema.`)}}function o(e,t,n,r){for(let i of t??[])a(e,i,n,r)}function s(e){let t=e.included===void 0||e.included===!0,n=e.required===void 0||e.required===!0,r=`default`in e&&e.default!==void 0;return t&&n&&!r}function c(e){return typeof e}function l(e,t){e.enum=t;let n=Array.from(new Set(t.map(c)));n.length===1?e.type=n[0]:n.length>1&&(e.type=n)}function u(e,n){let r=[];for(let i of e){if(typeof i!=`object`){r.push(i);continue}i.enabled!==!1&&(typeof i.enabled==`object`&&t(n,`An "options" entry has a predicate-based "enabled" flag; whether it's enabled can't be determined statically, so it was included in the JSON Schema "enum" anyway.`),r.push(i.value))}return r}function d(e){return typeof e==`object`&&!!e&&`type`in e}function f(e){return{anyOf:[e,{type:`null`}]}}function p(e,t){return t?{const:e,type:e===null?`null`:typeof e}:{const:e}}function m(e,t){let n={},r=[];for(let[i,a]of Object.entries(e)){if(h(a,t))continue;let e=y(a,t),o=s(a);n[i]=t.strict&&!o?f(e):e,(o||t.strict)&&r.push(i)}return{properties:n,required:r}}function h(t,n){return t.included===!1?!0:n.mode===`input`&&t.type===e.EXPRESSION}function g(e,t,n){if(e.private!==!0)return t;let r={type:`object`,properties:{state:p(`plain`,n.strict),value:t},required:n.strict?[`state`,`value`]:[`state`]},i={type:`object`,properties:{state:p(`masked`,n.strict),value:{type:`string`}},required:[`state`,`value`]};return n.strict&&(r.additionalProperties=!1,i.additionalProperties=!1),{[n.unionKeyword]:[r,i]}}function _(e,t){e.default!==void 0&&(t.default=e.default instanceof Date?e.default.toISOString():e.default)}function v(e,t){let n=e.meta;if(!n)return;let{id:r,title:i,description:a,deprecated:o,...s}=n;r!==void 0&&(t.$id=r),i!==void 0&&(t.title=i),a!==void 0&&(t.description=a),o!==void 0&&(t.deprecated=o),Object.assign(t,s)}function y(e,t){let n=b(e,t);return _(e,n),v(e,n),g(e,n,t)}function b(n,r){switch(n.type){case e.STRING:{let e={type:`string`};return o(e,n.rules,n.type,r),e}case e.NUMBER:{let e={type:`number`};return o(e,n.rules,n.type,r),e}case e.BOOLEAN:{let e={type:`boolean`};return o(e,n.rules,n.type,r),e}case e.DATE:{let e={type:`string`,format:`date-time`};return o(e,n.rules,n.type,r),e}case e.LITERAL:return p(n.value,r.strict);case e.ENUM:{let e={};return l(e,Object.values(n.enum)),o(e,n.rules,n.type,r),e}case e.OPTIONS:{let e={};return l(e,u(n.options,r)),o(e,n.rules,n.type,r),e}case e.FILE:{let e={type:`string`};return o(e,n.rules,n.type,r),e}case e.EXPRESSION:return{};case e.ARRAY:{let e={type:`array`,items:y(n.schema,r)};return o(e,n.rules,n.type,r),e}case e.OBJECT:{let{properties:e,required:t}=m(n.fields,r),i={type:`object`,properties:e};return t.length>0&&(i.required=t),r.strict&&(i.additionalProperties=!1),o(i,n.rules,n.type,r),i}case e.DISCRIMINATED_UNION:{let e=n.schemas.map(e=>{let t={};for(let[r,i]of Object.entries(e))r!==n.key&&d(i)&&(t[r]=i);let{properties:i,required:a}=m(t,r);i[n.key]=p(e[n.key],r.strict),a.unshift(n.key);let o={type:`object`,properties:i,required:a};return r.strict&&(o.additionalProperties=!1),o});return{[r.unionKeyword]:e}}default:return t(r,`Unknown schema type cannot be converted to JSON Schema.`),{}}}function x(e,t={}){return{$schema:`https://json-schema.org/draft/2020-12/schema`,...y(e,{errorMode:t.errorMode??`warn`,mode:t.mode??`input`,strict:t.strict??!0,unionKeyword:t.unionKeyword??`oneOf`})}}export{x as toStandardJsonSchema};
|
package/dist/index.mjs
CHANGED
|
@@ -114,6 +114,12 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
114
114
|
else reportIssue(context, `"equals" rule with a non-static value cannot be converted to JSON Schema.`);
|
|
115
115
|
break;
|
|
116
116
|
}
|
|
117
|
+
case "not_equals": {
|
|
118
|
+
const notEquals = resolveStatic(rule.notEquals);
|
|
119
|
+
if (notEquals.ok) setKeyword(jsonSchema, "not", { const: notEquals.value });
|
|
120
|
+
else reportIssue(context, `"not_equals" rule with a non-static value cannot be converted to JSON Schema.`);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
117
123
|
case "includes": {
|
|
118
124
|
const includes = resolveStatic(rule.includes);
|
|
119
125
|
if (!includes.ok) {
|
|
@@ -174,6 +180,8 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
174
180
|
case "max_date":
|
|
175
181
|
case "before":
|
|
176
182
|
case "after":
|
|
183
|
+
case "same_calendar":
|
|
184
|
+
case "boundary_day":
|
|
177
185
|
reportIssue(context, `"${rule.type}" rule has no JSON Schema equivalent (date comparisons aren't representable).`);
|
|
178
186
|
break;
|
|
179
187
|
case "min_size":
|
|
@@ -186,6 +194,9 @@ function applyRule(jsonSchema, rule, schemaType, context) {
|
|
|
186
194
|
case "conditional":
|
|
187
195
|
reportIssue(context, `"conditional" rule has no JSON Schema equivalent.`);
|
|
188
196
|
break;
|
|
197
|
+
case "satisfies":
|
|
198
|
+
reportIssue(context, `"satisfies" rule has no JSON Schema equivalent.`);
|
|
199
|
+
break;
|
|
189
200
|
default: reportIssue(context, `Unknown rule cannot be converted to JSON Schema.`);
|
|
190
201
|
}
|
|
191
202
|
}
|
|
@@ -198,7 +209,8 @@ function applyRules(jsonSchema, rules, schemaType, context) {
|
|
|
198
209
|
function isMandatory(schema) {
|
|
199
210
|
const isIncluded = schema.included === void 0 || schema.included === true;
|
|
200
211
|
const isRequired = schema.required === void 0 || schema.required === true;
|
|
201
|
-
|
|
212
|
+
const hasDefault = "default" in schema && schema.default !== void 0;
|
|
213
|
+
return isIncluded && isRequired && !hasDefault;
|
|
202
214
|
}
|
|
203
215
|
function jsonTypeOf(value) {
|
|
204
216
|
return typeof value;
|
|
@@ -211,47 +223,101 @@ function applyEnumValues(jsonSchema, values) {
|
|
|
211
223
|
}
|
|
212
224
|
function collectOptionValues(options, context) {
|
|
213
225
|
const values = [];
|
|
214
|
-
const
|
|
226
|
+
for (const option of options) {
|
|
215
227
|
if (typeof option !== "object") {
|
|
216
228
|
values.push(option);
|
|
217
|
-
|
|
229
|
+
continue;
|
|
218
230
|
}
|
|
219
|
-
if (option.enabled === false)
|
|
231
|
+
if (option.enabled === false) continue;
|
|
220
232
|
if (typeof option.enabled === "object") reportIssue(context, `An "options" entry has a predicate-based "enabled" flag; whether it's enabled can't be determined statically, so it was included in the JSON Schema "enum" anyway.`);
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
for (const option of options) visit(option);
|
|
233
|
+
values.push(option.value);
|
|
234
|
+
}
|
|
224
235
|
return values;
|
|
225
236
|
}
|
|
226
237
|
function isSchema(value) {
|
|
227
238
|
return typeof value === "object" && value !== null && "type" in value;
|
|
228
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Wraps a property's schema so it also accepts `null` — strict mode's only
|
|
242
|
+
* way to express "may be absent", since every property must be listed in
|
|
243
|
+
* `required`.
|
|
244
|
+
*/
|
|
245
|
+
function withNullable(jsonSchema) {
|
|
246
|
+
return { anyOf: [jsonSchema, { type: "null" }] };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* A `const` node, paired with its inferred `type` under `strict` (strict
|
|
250
|
+
* mode requires `type` everywhere; a bare `const` has none otherwise).
|
|
251
|
+
*/
|
|
252
|
+
function constNode(value, strict) {
|
|
253
|
+
return strict ? {
|
|
254
|
+
const: value,
|
|
255
|
+
type: value === null ? "null" : typeof value
|
|
256
|
+
} : { const: value };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Converts a set of object fields into `properties`/`required`, shared by
|
|
260
|
+
* the OBJECT case and each `discriminatedUnion()` member. Under `strict`,
|
|
261
|
+
* every non-omitted field is listed in `required` — one that isn't
|
|
262
|
+
* otherwise mandatory has its schema widened to also accept `null`.
|
|
263
|
+
*/
|
|
264
|
+
function convertObjectFields(fields, context) {
|
|
265
|
+
const properties = {};
|
|
266
|
+
const required = [];
|
|
267
|
+
for (const [key, fieldSchema] of Object.entries(fields)) {
|
|
268
|
+
if (shouldOmitField(fieldSchema, context)) continue;
|
|
269
|
+
const propertySchema = convertSchema(fieldSchema, context);
|
|
270
|
+
const mandatory = isMandatory(fieldSchema);
|
|
271
|
+
properties[key] = context.strict && !mandatory ? withNullable(propertySchema) : propertySchema;
|
|
272
|
+
if (mandatory || context.strict) required.push(key);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
properties,
|
|
276
|
+
required
|
|
277
|
+
};
|
|
278
|
+
}
|
|
229
279
|
function shouldOmitField(schema, context) {
|
|
230
280
|
if (schema.included === false) return true;
|
|
231
281
|
return context.mode === "input" && schema.type === SchemaType.EXPRESSION;
|
|
232
282
|
}
|
|
233
|
-
function applyPrivacyWrapper(schema, innerSchema) {
|
|
283
|
+
function applyPrivacyWrapper(schema, innerSchema, context) {
|
|
234
284
|
if (schema.private !== true) return innerSchema;
|
|
235
|
-
|
|
285
|
+
const plain = {
|
|
236
286
|
type: "object",
|
|
237
287
|
properties: {
|
|
238
|
-
state:
|
|
288
|
+
state: constNode("plain", context.strict),
|
|
239
289
|
value: innerSchema
|
|
240
290
|
},
|
|
241
|
-
required: ["state"]
|
|
242
|
-
}
|
|
291
|
+
required: context.strict ? ["state", "value"] : ["state"]
|
|
292
|
+
};
|
|
293
|
+
const masked = {
|
|
243
294
|
type: "object",
|
|
244
295
|
properties: {
|
|
245
|
-
state:
|
|
296
|
+
state: constNode("masked", context.strict),
|
|
246
297
|
value: { type: "string" }
|
|
247
298
|
},
|
|
248
299
|
required: ["state", "value"]
|
|
249
|
-
}
|
|
300
|
+
};
|
|
301
|
+
if (context.strict) {
|
|
302
|
+
plain.additionalProperties = false;
|
|
303
|
+
masked.additionalProperties = false;
|
|
304
|
+
}
|
|
305
|
+
return { [context.unionKeyword]: [plain, masked] };
|
|
250
306
|
}
|
|
251
307
|
function applyDefault(schema, jsonSchema) {
|
|
252
308
|
if (schema.default === void 0) return;
|
|
253
309
|
jsonSchema.default = schema.default instanceof Date ? schema.default.toISOString() : schema.default;
|
|
254
310
|
}
|
|
311
|
+
function applyMeta(schema, jsonSchema) {
|
|
312
|
+
const meta = schema.meta;
|
|
313
|
+
if (!meta) return;
|
|
314
|
+
const { id, title, description, deprecated, ...rest } = meta;
|
|
315
|
+
if (id !== void 0) jsonSchema.$id = id;
|
|
316
|
+
if (title !== void 0) jsonSchema.title = title;
|
|
317
|
+
if (description !== void 0) jsonSchema.description = description;
|
|
318
|
+
if (deprecated !== void 0) jsonSchema.deprecated = deprecated;
|
|
319
|
+
Object.assign(jsonSchema, rest);
|
|
320
|
+
}
|
|
255
321
|
/**
|
|
256
322
|
* Converts a dynz schema to a JSON Schema (2020-12) document. Rule values
|
|
257
323
|
* and schema kinds without a JSON Schema equivalent are handled per
|
|
@@ -260,7 +326,8 @@ function applyDefault(schema, jsonSchema) {
|
|
|
260
326
|
function convertSchema(schema, context) {
|
|
261
327
|
const jsonSchema = convertSchemaKind(schema, context);
|
|
262
328
|
applyDefault(schema, jsonSchema);
|
|
263
|
-
|
|
329
|
+
applyMeta(schema, jsonSchema);
|
|
330
|
+
return applyPrivacyWrapper(schema, jsonSchema, context);
|
|
264
331
|
}
|
|
265
332
|
function convertSchemaKind(schema, context) {
|
|
266
333
|
switch (schema.type) {
|
|
@@ -287,7 +354,7 @@ function convertSchemaKind(schema, context) {
|
|
|
287
354
|
applyRules(jsonSchema, schema.rules, schema.type, context);
|
|
288
355
|
return jsonSchema;
|
|
289
356
|
}
|
|
290
|
-
case SchemaType.LITERAL: return
|
|
357
|
+
case SchemaType.LITERAL: return constNode(schema.value, context.strict);
|
|
291
358
|
case SchemaType.ENUM: {
|
|
292
359
|
const jsonSchema = {};
|
|
293
360
|
applyEnumValues(jsonSchema, Object.values(schema.enum));
|
|
@@ -315,35 +382,33 @@ function convertSchemaKind(schema, context) {
|
|
|
315
382
|
return jsonSchema;
|
|
316
383
|
}
|
|
317
384
|
case SchemaType.OBJECT: {
|
|
318
|
-
const properties =
|
|
319
|
-
const required = [];
|
|
320
|
-
for (const [key, fieldSchema] of Object.entries(schema.fields)) {
|
|
321
|
-
if (shouldOmitField(fieldSchema, context)) continue;
|
|
322
|
-
properties[key] = convertSchema(fieldSchema, context);
|
|
323
|
-
if (isMandatory(fieldSchema)) required.push(key);
|
|
324
|
-
}
|
|
385
|
+
const { properties, required } = convertObjectFields(schema.fields, context);
|
|
325
386
|
const jsonSchema = {
|
|
326
387
|
type: "object",
|
|
327
388
|
properties
|
|
328
389
|
};
|
|
329
390
|
if (required.length > 0) jsonSchema.required = required;
|
|
391
|
+
if (context.strict) jsonSchema.additionalProperties = false;
|
|
330
392
|
applyRules(jsonSchema, schema.rules, schema.type, context);
|
|
331
393
|
return jsonSchema;
|
|
332
394
|
}
|
|
333
|
-
case SchemaType.DISCRIMINATED_UNION:
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
properties[key] =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
395
|
+
case SchemaType.DISCRIMINATED_UNION: {
|
|
396
|
+
const members = schema.schemas.map((member) => {
|
|
397
|
+
const memberFields = {};
|
|
398
|
+
for (const [key, value] of Object.entries(member)) if (key !== schema.key && isSchema(value)) memberFields[key] = value;
|
|
399
|
+
const { properties, required } = convertObjectFields(memberFields, context);
|
|
400
|
+
properties[schema.key] = constNode(member[schema.key], context.strict);
|
|
401
|
+
required.unshift(schema.key);
|
|
402
|
+
const jsonSchema = {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties,
|
|
405
|
+
required
|
|
406
|
+
};
|
|
407
|
+
if (context.strict) jsonSchema.additionalProperties = false;
|
|
408
|
+
return jsonSchema;
|
|
409
|
+
});
|
|
410
|
+
return { [context.unionKeyword]: members };
|
|
411
|
+
}
|
|
347
412
|
default:
|
|
348
413
|
reportIssue(context, `Unknown schema type cannot be converted to JSON Schema.`);
|
|
349
414
|
return {};
|
|
@@ -359,14 +424,19 @@ function convertSchemaKind(schema, context) {
|
|
|
359
424
|
* transformers) and schema kinds without a JSON Schema equivalent are
|
|
360
425
|
* handled according to `config.errorMode` (default `"warn"`). Whether
|
|
361
426
|
* `expression` fields are included is controlled by `config.mode`
|
|
362
|
-
* (default `"input"`, which omits them).
|
|
427
|
+
* (default `"input"`, which omits them). `config.strict` (default `true`)
|
|
428
|
+
* produces output compatible with LLM structured-output "strict" modes,
|
|
429
|
+
* and `config.unionKeyword` (default `"oneOf"`) controls whether schema-
|
|
430
|
+
* selection unions use `oneOf` or `anyOf`.
|
|
363
431
|
*/
|
|
364
432
|
function toStandardJsonSchema(schema, config = {}) {
|
|
365
433
|
return {
|
|
366
434
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
367
435
|
...convertSchema(schema, {
|
|
368
436
|
errorMode: config.errorMode ?? "warn",
|
|
369
|
-
mode: config.mode ?? "input"
|
|
437
|
+
mode: config.mode ?? "input",
|
|
438
|
+
strict: config.strict ?? true,
|
|
439
|
+
unionKeyword: config.unionKeyword ?? "oneOf"
|
|
370
440
|
})
|
|
371
441
|
};
|
|
372
442
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynz/to-json-schema",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
|
-
"
|
|
43
|
-
"dynz": "
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"dynz": "1.2.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"tsdown": "^0.16.5"
|