@powerlines/schema 0.11.1 → 0.11.2
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 +36 -22
- package/dist/bundle.d.cts +2 -2
- package/dist/bundle.d.cts.map +1 -1
- package/dist/bundle.d.mts +2 -2
- package/dist/bundle.d.mts.map +1 -1
- package/dist/bundle.mjs.map +1 -1
- package/dist/codegen.cjs +27 -0
- package/dist/codegen.d.cts +15 -0
- package/dist/codegen.d.cts.map +1 -0
- package/dist/codegen.d.mts +15 -0
- package/dist/codegen.d.mts.map +1 -0
- package/dist/codegen.mjs +24 -0
- package/dist/codegen.mjs.map +1 -0
- package/dist/extract.cjs +201 -52
- package/dist/extract.d.cts +81 -16
- package/dist/extract.d.cts.map +1 -1
- package/dist/extract.d.mts +81 -16
- package/dist/extract.d.mts.map +1 -1
- package/dist/extract.mjs +199 -55
- package/dist/extract.mjs.map +1 -1
- package/dist/index.cjs +15 -3
- package/dist/index.d.cts +7 -5
- package/dist/index.d.mts +7 -5
- package/dist/index.mjs +6 -4
- package/dist/jtd.cjs +385 -0
- package/dist/jtd.d.cts +15 -0
- package/dist/jtd.d.cts.map +1 -0
- package/dist/jtd.d.mts +15 -0
- package/dist/jtd.d.mts.map +1 -0
- package/dist/jtd.mjs +384 -0
- package/dist/jtd.mjs.map +1 -0
- package/dist/reflection.cjs +321 -100
- package/dist/reflection.d.cts +16 -13
- package/dist/reflection.d.cts.map +1 -1
- package/dist/reflection.d.mts +16 -13
- package/dist/reflection.d.mts.map +1 -1
- package/dist/reflection.mjs +323 -101
- package/dist/reflection.mjs.map +1 -1
- package/dist/resolve.d.cts +5 -5
- package/dist/resolve.d.cts.map +1 -1
- package/dist/resolve.d.mts +5 -5
- package/dist/resolve.d.mts.map +1 -1
- package/dist/resolve.mjs.map +1 -1
- package/dist/type-checks.cjs +76 -0
- package/dist/type-checks.d.cts +43 -0
- package/dist/type-checks.d.cts.map +1 -0
- package/dist/type-checks.d.mts +43 -0
- package/dist/type-checks.d.mts.map +1 -0
- package/dist/type-checks.mjs +71 -0
- package/dist/type-checks.mjs.map +1 -0
- package/dist/types.d.cts +166 -24
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +166 -24
- package/dist/types.d.mts.map +1 -1
- package/package.json +25 -19
- package/dist/is-schema-definition.cjs +0 -18
- package/dist/is-schema-definition.d.cts +0 -13
- package/dist/is-schema-definition.d.cts.map +0 -1
- package/dist/is-schema-definition.d.mts +0 -13
- package/dist/is-schema-definition.d.mts.map +0 -1
- package/dist/is-schema-definition.mjs +0 -17
- package/dist/is-schema-definition.mjs.map +0 -1
package/dist/reflection.cjs
CHANGED
|
@@ -1,123 +1,238 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
3
|
let _powerlines_deepkit_vendor_type = require("@powerlines/deepkit/vendor/type");
|
|
4
|
+
let _stryke_type_checks = require("@stryke/type-checks");
|
|
4
5
|
|
|
5
6
|
//#region src/reflection.ts
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Maps a Deepkit numeric `brand` to the JTD numeric `type` keyword that best preserves the underlying width and signedness.
|
|
9
|
+
*
|
|
10
|
+
* @param brand - The Deepkit `TypeNumberBrand` of a numeric reflection, or `undefined` when no brand is set.
|
|
11
|
+
* @returns The JTD numeric `type` keyword to use.
|
|
12
|
+
*/
|
|
13
|
+
function numberBrandToJtdType(brand) {
|
|
14
|
+
switch (brand) {
|
|
15
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.integer: return "int32";
|
|
16
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.int8: return "int8";
|
|
17
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.uint8: return "uint8";
|
|
18
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.int16: return "int16";
|
|
19
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.uint16: return "uint16";
|
|
20
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.int32: return "int32";
|
|
21
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.uint32: return "uint32";
|
|
22
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.float:
|
|
23
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.float32: return "float32";
|
|
24
|
+
case _powerlines_deepkit_vendor_type.TypeNumberBrand.float64: return "float64";
|
|
25
|
+
case void 0:
|
|
26
|
+
default: return "float64";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Converts a Deepkit type reflection into a JSON Type Definition (RFC 8927) form suitable for AJV's JTD validator.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Some TypeScript constructs have no direct JTD equivalent and are handled with the closest available form:
|
|
34
|
+
*
|
|
35
|
+
* - `null` and `undefined` become the empty JTD form with `nullable: true`.
|
|
36
|
+
* - Unions of primitives that cannot be expressed as a JTD enum collapse to the empty form (which validates any value).
|
|
37
|
+
* - String/number/bigint literal unions are emitted as a JTD enum (non-string members are stringified, as JTD requires string enum members).
|
|
38
|
+
* - Tuples are emitted as a JTD elements form whose element schema is the single tuple member type, or the empty schema for mixed tuples.
|
|
39
|
+
* - `Date` is emitted as `{ type: "timestamp" }`.
|
|
40
|
+
* - Discriminated unions of object literals (a shared string-literal tag property) are emitted as a JTD discriminator form.
|
|
8
41
|
*
|
|
9
42
|
* @param reflection - The Deepkit type reflection to convert.
|
|
10
|
-
* @returns The corresponding
|
|
43
|
+
* @returns The corresponding JTD form, or `undefined` if the type cannot be represented.
|
|
11
44
|
*/
|
|
12
45
|
function reflectionToJsonSchema(reflection) {
|
|
46
|
+
return reflectionToJtd(reflection);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Internal worker that performs the recursive Deepkit reflection → JTD conversion.
|
|
50
|
+
*
|
|
51
|
+
* @param reflection - The Deepkit type reflection to convert.
|
|
52
|
+
* @returns The corresponding JTD form, or `undefined` if the type cannot be represented.
|
|
53
|
+
*/
|
|
54
|
+
function reflectionToJtd(reflection) {
|
|
55
|
+
const schema = {};
|
|
56
|
+
if ((0, _stryke_type_checks.isSetObject)(reflection?.tags)) {
|
|
57
|
+
const tags = reflection.tags;
|
|
58
|
+
schema.metadata = schema.metadata ?? {};
|
|
59
|
+
if (tags.readonly === true) schema.metadata.isReadonly = true;
|
|
60
|
+
if (tags.ignore === true) schema.metadata.isIgnored = true;
|
|
61
|
+
if (tags.internal === true) schema.metadata.isInternal = true;
|
|
62
|
+
if (tags.runtime === true) schema.metadata.isRuntime = true;
|
|
63
|
+
if (tags.hidden === true) schema.metadata.isHidden = true;
|
|
64
|
+
if ((0, _stryke_type_checks.isSetArray)(tags.alias)) schema.metadata.alias = tags.alias;
|
|
65
|
+
if ((0, _stryke_type_checks.isSetString)(tags.title)) schema.metadata.title = tags.title;
|
|
66
|
+
}
|
|
13
67
|
switch (reflection.kind) {
|
|
14
68
|
case _powerlines_deepkit_vendor_type.ReflectionKind.any:
|
|
15
69
|
case _powerlines_deepkit_vendor_type.ReflectionKind.unknown:
|
|
16
70
|
case _powerlines_deepkit_vendor_type.ReflectionKind.void:
|
|
17
71
|
case _powerlines_deepkit_vendor_type.ReflectionKind.object: return {};
|
|
18
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.never:
|
|
19
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.undefined:
|
|
20
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.null: return {
|
|
21
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.string: return {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
72
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.never: return;
|
|
73
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.undefined:
|
|
74
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.null: return { nullable: true };
|
|
75
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.string: return {
|
|
76
|
+
...schema,
|
|
77
|
+
type: "string"
|
|
78
|
+
};
|
|
79
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.boolean: return {
|
|
80
|
+
...schema,
|
|
81
|
+
type: "boolean"
|
|
82
|
+
};
|
|
83
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.number: return {
|
|
84
|
+
...schema,
|
|
85
|
+
type: numberBrandToJtdType(reflection.brand)
|
|
86
|
+
};
|
|
27
87
|
case _powerlines_deepkit_vendor_type.ReflectionKind.bigint: return {
|
|
28
|
-
|
|
29
|
-
|
|
88
|
+
...schema,
|
|
89
|
+
type: "float64"
|
|
90
|
+
};
|
|
91
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.regexp: return {
|
|
92
|
+
...schema,
|
|
93
|
+
type: "string"
|
|
30
94
|
};
|
|
31
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.regexp: return { type: "string" };
|
|
32
95
|
case _powerlines_deepkit_vendor_type.ReflectionKind.literal: {
|
|
33
96
|
const { literal } = reflection;
|
|
34
97
|
if (typeof literal === "string") return {
|
|
35
|
-
|
|
36
|
-
|
|
98
|
+
...schema,
|
|
99
|
+
enum: [literal]
|
|
37
100
|
};
|
|
38
|
-
if (typeof literal === "number") return {
|
|
39
|
-
|
|
40
|
-
|
|
101
|
+
if (typeof literal === "number" || typeof literal === "bigint") return {
|
|
102
|
+
...schema,
|
|
103
|
+
enum: [String(literal)]
|
|
41
104
|
};
|
|
42
105
|
if (typeof literal === "boolean") return {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
if (typeof literal === "bigint") return {
|
|
47
|
-
type: "integer",
|
|
48
|
-
format: "int64"
|
|
106
|
+
...schema,
|
|
107
|
+
type: "boolean"
|
|
49
108
|
};
|
|
50
109
|
if (literal instanceof RegExp) return {
|
|
51
|
-
|
|
52
|
-
|
|
110
|
+
...schema,
|
|
111
|
+
type: "string"
|
|
53
112
|
};
|
|
54
|
-
return
|
|
113
|
+
return schema;
|
|
55
114
|
}
|
|
56
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.templateLiteral: return {
|
|
115
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.templateLiteral: return {
|
|
116
|
+
...schema,
|
|
117
|
+
type: "string"
|
|
118
|
+
};
|
|
57
119
|
case _powerlines_deepkit_vendor_type.ReflectionKind.enum: {
|
|
58
|
-
const values = reflection.values.filter((value) => typeof value === "string" || typeof value === "number");
|
|
59
|
-
const
|
|
60
|
-
|
|
120
|
+
const values = reflection.values.filter((value) => typeof value === "string" || typeof value === "number").map((value) => String(value));
|
|
121
|
+
const unique = Array.from(new Set(values));
|
|
122
|
+
if (unique.length === 0) return schema;
|
|
61
123
|
return {
|
|
62
|
-
|
|
63
|
-
enum:
|
|
124
|
+
...schema,
|
|
125
|
+
enum: unique
|
|
64
126
|
};
|
|
65
127
|
}
|
|
66
128
|
case _powerlines_deepkit_vendor_type.ReflectionKind.array: {
|
|
67
|
-
const items =
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
items
|
|
71
|
-
}
|
|
129
|
+
const items = reflectionToJtd(reflection.type);
|
|
130
|
+
return {
|
|
131
|
+
...schema,
|
|
132
|
+
elements: items ?? {}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.tuple: {
|
|
136
|
+
const items = reflection.types.map((member) => reflectionToJtd(member.type)).filter((item) => item !== void 0);
|
|
137
|
+
if (items.length === 0) return {
|
|
138
|
+
...schema,
|
|
139
|
+
elements: {}
|
|
140
|
+
};
|
|
141
|
+
if (items.length === 1) return {
|
|
142
|
+
...schema,
|
|
143
|
+
elements: items[0]
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
...schema,
|
|
147
|
+
elements: {}
|
|
148
|
+
};
|
|
72
149
|
}
|
|
73
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.tuple: return {
|
|
74
|
-
type: "array",
|
|
75
|
-
items: reflection.types.map((member) => reflectionToJsonSchema(member.type)).filter((item) => item !== void 0),
|
|
76
|
-
minItems: reflection.types.filter((member) => !member.optional).length,
|
|
77
|
-
maxItems: reflection.types.length
|
|
78
|
-
};
|
|
79
150
|
case _powerlines_deepkit_vendor_type.ReflectionKind.union: {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return {
|
|
151
|
+
const branches = reflection.types.map((inner) => reflectionToJtd(inner)).filter((item) => item !== void 0);
|
|
152
|
+
const nullable = reflection.types.some((inner) => inner.kind === _powerlines_deepkit_vendor_type.ReflectionKind.null || inner.kind === _powerlines_deepkit_vendor_type.ReflectionKind.undefined);
|
|
153
|
+
const nonNull = branches.filter((b) => !isPureNullable(b));
|
|
154
|
+
if (nonNull.length === 0) return {
|
|
155
|
+
...schema,
|
|
156
|
+
nullable: true
|
|
157
|
+
};
|
|
158
|
+
if (nonNull.length === 1) {
|
|
159
|
+
const only = nonNull[0];
|
|
160
|
+
if (nullable) only.nullable = true;
|
|
161
|
+
return {
|
|
162
|
+
...schema,
|
|
163
|
+
...only
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (nonNull.every(isEnumForm)) {
|
|
167
|
+
const merged = Array.from(new Set(nonNull.flatMap((b) => b.enum)));
|
|
168
|
+
const form = {
|
|
169
|
+
...schema,
|
|
170
|
+
enum: merged
|
|
171
|
+
};
|
|
172
|
+
if (nullable) form.nullable = true;
|
|
173
|
+
return {
|
|
174
|
+
...schema,
|
|
175
|
+
...form
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const discriminator = tryReflectionDiscriminator(reflection.types);
|
|
179
|
+
if (discriminator) {
|
|
180
|
+
if (nullable) discriminator.nullable = true;
|
|
181
|
+
return {
|
|
182
|
+
...schema,
|
|
183
|
+
...discriminator
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const fallback = {};
|
|
187
|
+
if (nullable) fallback.nullable = true;
|
|
188
|
+
return {
|
|
189
|
+
...schema,
|
|
190
|
+
...fallback
|
|
191
|
+
};
|
|
84
192
|
}
|
|
85
193
|
case _powerlines_deepkit_vendor_type.ReflectionKind.intersection: {
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
if (
|
|
89
|
-
|
|
194
|
+
const members = reflection.types.map((inner) => reflectionToJtd(inner)).filter((item) => item !== void 0);
|
|
195
|
+
if (members.length === 0) return;
|
|
196
|
+
if (members.length === 1) return {
|
|
197
|
+
...schema,
|
|
198
|
+
...members[0]
|
|
199
|
+
};
|
|
200
|
+
if (members.every((member) => member && isPropertiesForm(member))) return mergePropertiesForms(members);
|
|
201
|
+
return {
|
|
202
|
+
...schema,
|
|
203
|
+
...members[0]
|
|
204
|
+
};
|
|
90
205
|
}
|
|
91
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.promise: return
|
|
92
|
-
case _powerlines_deepkit_vendor_type.ReflectionKind.objectLiteral: return
|
|
206
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.promise: return reflectionToJtd(reflection.type);
|
|
207
|
+
case _powerlines_deepkit_vendor_type.ReflectionKind.objectLiteral: return objectReflectionToJtd(reflection);
|
|
93
208
|
case _powerlines_deepkit_vendor_type.ReflectionKind.class: switch (reflection.classType?.name) {
|
|
94
209
|
case "Date": return {
|
|
95
|
-
|
|
96
|
-
|
|
210
|
+
...schema,
|
|
211
|
+
type: "timestamp"
|
|
212
|
+
};
|
|
213
|
+
case "RegExp": return {
|
|
214
|
+
...schema,
|
|
215
|
+
type: "string"
|
|
97
216
|
};
|
|
98
|
-
case "RegExp": return { type: "string" };
|
|
99
217
|
case "URL": return {
|
|
100
|
-
|
|
101
|
-
|
|
218
|
+
...schema,
|
|
219
|
+
type: "string"
|
|
102
220
|
};
|
|
103
221
|
case "Set": {
|
|
104
222
|
const itemType = reflection.arguments?.[0];
|
|
105
|
-
const items = itemType ?
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
items
|
|
110
|
-
} : {
|
|
111
|
-
type: "array",
|
|
112
|
-
uniqueItems: true
|
|
223
|
+
const items = itemType ? reflectionToJtd(itemType) : void 0;
|
|
224
|
+
return {
|
|
225
|
+
...schema,
|
|
226
|
+
elements: items ?? {}
|
|
113
227
|
};
|
|
114
228
|
}
|
|
115
229
|
case "Map": {
|
|
116
230
|
const valueType = reflection.arguments?.[1];
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
231
|
+
const values = valueType ? reflectionToJtd(valueType) : void 0;
|
|
232
|
+
return {
|
|
233
|
+
...schema,
|
|
234
|
+
values: values ?? {}
|
|
235
|
+
};
|
|
121
236
|
}
|
|
122
237
|
case "Uint8Array":
|
|
123
238
|
case "Uint8ClampedArray":
|
|
@@ -130,11 +245,11 @@ function reflectionToJsonSchema(reflection) {
|
|
|
130
245
|
case "Float64Array":
|
|
131
246
|
case "BigInt64Array":
|
|
132
247
|
case "BigUint64Array": return {
|
|
133
|
-
|
|
134
|
-
|
|
248
|
+
...schema,
|
|
249
|
+
type: "string"
|
|
135
250
|
};
|
|
136
251
|
case void 0:
|
|
137
|
-
default: return
|
|
252
|
+
default: return objectReflectionToJtd(reflection);
|
|
138
253
|
}
|
|
139
254
|
case _powerlines_deepkit_vendor_type.ReflectionKind.symbol:
|
|
140
255
|
case _powerlines_deepkit_vendor_type.ReflectionKind.property:
|
|
@@ -154,38 +269,144 @@ function reflectionToJsonSchema(reflection) {
|
|
|
154
269
|
}
|
|
155
270
|
}
|
|
156
271
|
/**
|
|
157
|
-
*
|
|
272
|
+
* Tests whether a JTD form is an enum form.
|
|
273
|
+
*
|
|
274
|
+
* @param form - The JTD form to inspect.
|
|
275
|
+
* @returns `true` if the form is a JTD enum form.
|
|
276
|
+
*/
|
|
277
|
+
function isEnumForm(form) {
|
|
278
|
+
return Array.isArray(form.enum);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Tests whether a JTD form is a properties form (object).
|
|
282
|
+
*
|
|
283
|
+
* @param form - The JTD form to inspect.
|
|
284
|
+
* @returns `true` if the form is a JTD properties form.
|
|
285
|
+
*/
|
|
286
|
+
function isPropertiesForm(form) {
|
|
287
|
+
return "properties" in form || "optionalProperties" in form;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Tests whether a JTD form is the empty `{ nullable: true }` placeholder.
|
|
291
|
+
*
|
|
292
|
+
* @param form - The JTD form to inspect.
|
|
293
|
+
* @returns `true` if the form has no shape constraints beyond `nullable`.
|
|
294
|
+
*/
|
|
295
|
+
function isPureNullable(form) {
|
|
296
|
+
return Object.keys(form).filter((k) => k !== "nullable" && k !== "metadata").length === 0 && form.nullable === true;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Shallow-merges two JTD properties forms, unioning their `properties` and `optionalProperties` maps.
|
|
300
|
+
*
|
|
301
|
+
* @param forms - The JTD properties forms to merge.
|
|
302
|
+
* @returns The merged JTD properties form.
|
|
303
|
+
*/
|
|
304
|
+
function mergePropertiesForms(forms) {
|
|
305
|
+
const merged = {
|
|
306
|
+
properties: {},
|
|
307
|
+
optionalProperties: {}
|
|
308
|
+
};
|
|
309
|
+
for (const form of forms) {
|
|
310
|
+
const p = form.properties;
|
|
311
|
+
const o = form.optionalProperties;
|
|
312
|
+
if (p) Object.assign(merged.properties, p);
|
|
313
|
+
if (o) Object.assign(merged.optionalProperties, o);
|
|
314
|
+
if (form.additionalProperties) merged.additionalProperties = true;
|
|
315
|
+
}
|
|
316
|
+
const hasProperties = Object.keys(merged.properties).length > 0;
|
|
317
|
+
const hasOptional = Object.keys(merged.optionalProperties).length > 0;
|
|
318
|
+
const result = {};
|
|
319
|
+
if (hasProperties) result.properties = merged.properties;
|
|
320
|
+
else if (!hasOptional) result.properties = {};
|
|
321
|
+
if (hasOptional) result.optionalProperties = merged.optionalProperties;
|
|
322
|
+
if (merged.additionalProperties) result.additionalProperties = true;
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Detects whether a Deepkit union represents a tagged union and, when so, emits the corresponding JTD discriminator form.
|
|
327
|
+
*
|
|
328
|
+
* @param types - The Deepkit reflection types that make up the union branches.
|
|
329
|
+
* @returns A JTD discriminator form if every non-null branch is an object literal that shares a string-literal tag property, otherwise `undefined`.
|
|
330
|
+
*/
|
|
331
|
+
function tryReflectionDiscriminator(types) {
|
|
332
|
+
const nonNullTypes = types.filter((t) => t.kind !== _powerlines_deepkit_vendor_type.ReflectionKind.null && t.kind !== _powerlines_deepkit_vendor_type.ReflectionKind.undefined);
|
|
333
|
+
const objectBranches = nonNullTypes.filter((t) => t.kind === _powerlines_deepkit_vendor_type.ReflectionKind.objectLiteral || t.kind === _powerlines_deepkit_vendor_type.ReflectionKind.class);
|
|
334
|
+
if (objectBranches.length < 2 || objectBranches.length !== nonNullTypes.length) return;
|
|
335
|
+
let tagKey;
|
|
336
|
+
const mapping = {};
|
|
337
|
+
for (const branch of objectBranches) {
|
|
338
|
+
const literalProps = [];
|
|
339
|
+
for (const member of branch.types) if ((member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.property || member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.propertySignature) && typeof member.name === "string" && member.type.kind === _powerlines_deepkit_vendor_type.ReflectionKind.literal && typeof member.type.literal === "string") literalProps.push({
|
|
340
|
+
name: member.name,
|
|
341
|
+
literal: member.type.literal
|
|
342
|
+
});
|
|
343
|
+
if (literalProps.length === 0) return;
|
|
344
|
+
const first = literalProps[0];
|
|
345
|
+
if (!tagKey) tagKey = first.name;
|
|
346
|
+
else if (tagKey !== first.name) return;
|
|
347
|
+
const body = objectReflectionToJtd({
|
|
348
|
+
...branch,
|
|
349
|
+
types: branch.types.filter((member) => !((member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.property || member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.propertySignature) && member.name === tagKey))
|
|
350
|
+
});
|
|
351
|
+
if (!body || !isPropertiesForm(body)) return;
|
|
352
|
+
mapping[first.literal] = body;
|
|
353
|
+
}
|
|
354
|
+
if (!tagKey) return;
|
|
355
|
+
return {
|
|
356
|
+
discriminator: tagKey,
|
|
357
|
+
mapping
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Internal worker that produces a JTD properties form (or `values` form for index signatures alone) from a Deepkit object-like type.
|
|
158
362
|
*
|
|
159
363
|
* @param type - The class or object literal type whose members should be serialized.
|
|
160
|
-
* @returns A
|
|
364
|
+
* @returns A JTD properties or values form describing the type's members.
|
|
161
365
|
*/
|
|
162
|
-
function
|
|
366
|
+
function objectReflectionToJtd(type) {
|
|
367
|
+
const reflection = _powerlines_deepkit_vendor_type.ReflectionClass.from(type);
|
|
368
|
+
const schema = {};
|
|
369
|
+
schema.metadata = schema.metadata ?? {};
|
|
370
|
+
schema.metadata.isReadonly = reflection.isReadonly();
|
|
371
|
+
schema.metadata.isIgnored = reflection.isIgnored();
|
|
372
|
+
schema.metadata.isInternal = reflection.isInternal();
|
|
373
|
+
schema.metadata.isRuntime = reflection.isRuntime();
|
|
374
|
+
schema.metadata.isHidden = reflection.isHidden();
|
|
375
|
+
if ((0, _stryke_type_checks.isSetString)(reflection.databaseSchemaName)) schema.metadata.table = reflection.databaseSchemaName;
|
|
376
|
+
if ((0, _stryke_type_checks.isSetString)(reflection.getDescription())) schema.metadata.description = reflection.getDescription();
|
|
377
|
+
if ((0, _stryke_type_checks.isSetArray)(reflection.getAlias())) schema.metadata.alias = reflection.getAlias();
|
|
378
|
+
if ((0, _stryke_type_checks.isSetString)(reflection.getTitle())) schema.metadata.title = reflection.getTitle();
|
|
163
379
|
const properties = {};
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (!property
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
380
|
+
const optionalProperties = {};
|
|
381
|
+
for (const propertyReflection of reflection.getProperties()) if (propertyReflection.getKind() === _powerlines_deepkit_vendor_type.ReflectionKind.indexSignature) {
|
|
382
|
+
const valueSchema = reflectionToJtd(propertyReflection.type);
|
|
383
|
+
if (valueSchema) return {
|
|
384
|
+
...schema,
|
|
385
|
+
values: valueSchema,
|
|
386
|
+
additionalProperties: true
|
|
387
|
+
};
|
|
388
|
+
} else {
|
|
389
|
+
const property = reflectionToJtd(propertyReflection.type);
|
|
390
|
+
if (!property) continue;
|
|
391
|
+
property.metadata = property.metadata ?? {};
|
|
392
|
+
property.metadata.isReadonly = propertyReflection.isReadonly();
|
|
393
|
+
property.metadata.isIgnored = propertyReflection.isIgnored();
|
|
394
|
+
property.metadata.isInternal = propertyReflection.isInternal();
|
|
395
|
+
property.metadata.isRuntime = propertyReflection.isRuntime();
|
|
396
|
+
property.metadata.isPrimaryKey = propertyReflection.isPrimaryKey();
|
|
397
|
+
property.metadata.isHidden = propertyReflection.isHidden();
|
|
398
|
+
if (propertyReflection.hasDefault()) property.metadata.default = propertyReflection.getDefaultValue();
|
|
399
|
+
if ((0, _stryke_type_checks.isSetString)(propertyReflection.getDescription())) property.metadata.description = propertyReflection.getDescription();
|
|
400
|
+
if ((0, _stryke_type_checks.isSetArray)(propertyReflection.getAlias())) property.metadata.alias = propertyReflection.getAlias();
|
|
401
|
+
if ((0, _stryke_type_checks.isSetString)(propertyReflection.getTitle())) property.metadata.title = propertyReflection.getTitle();
|
|
402
|
+
if (propertyReflection.isOptional()) optionalProperties[propertyReflection.name] = property;
|
|
403
|
+
else properties[propertyReflection.name] = property;
|
|
178
404
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
183
|
-
if (required.length > 0) schema.required = required;
|
|
184
|
-
if (additionalProperties !== void 0) schema.additionalProperties = additionalProperties;
|
|
185
|
-
if (description) schema.description = description;
|
|
405
|
+
if (Object.keys(properties).length > 0) schema.properties = properties;
|
|
406
|
+
else if (Object.keys(optionalProperties).length > 0) schema.optionalProperties = optionalProperties;
|
|
407
|
+
else schema.properties = {};
|
|
186
408
|
return schema;
|
|
187
409
|
}
|
|
188
410
|
|
|
189
411
|
//#endregion
|
|
190
|
-
exports.objectReflectionToJsonSchema = objectReflectionToJsonSchema;
|
|
191
412
|
exports.reflectionToJsonSchema = reflectionToJsonSchema;
|
package/dist/reflection.d.cts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { JTDSchemaType, SchemaMetadata } from "./types.cjs";
|
|
2
|
+
import { Type } from "@powerlines/deepkit/vendor/type";
|
|
3
3
|
|
|
4
4
|
//#region src/reflection.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* Converts a Deepkit type reflection into a JSON
|
|
6
|
+
* Converts a Deepkit type reflection into a JSON Type Definition (RFC 8927) form suitable for AJV's JTD validator.
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
declare function reflectionToJsonSchema(reflection: Type): JsonSchema7Type | undefined;
|
|
12
|
-
/**
|
|
13
|
-
* Builds a JSON Schema object representation from a Deepkit class or object literal type.
|
|
8
|
+
* @remarks
|
|
9
|
+
* Some TypeScript constructs have no direct JTD equivalent and are handled with the closest available form:
|
|
14
10
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* - `null` and `undefined` become the empty JTD form with `nullable: true`.
|
|
12
|
+
* - Unions of primitives that cannot be expressed as a JTD enum collapse to the empty form (which validates any value).
|
|
13
|
+
* - String/number/bigint literal unions are emitted as a JTD enum (non-string members are stringified, as JTD requires string enum members).
|
|
14
|
+
* - Tuples are emitted as a JTD elements form whose element schema is the single tuple member type, or the empty schema for mixed tuples.
|
|
15
|
+
* - `Date` is emitted as `{ type: "timestamp" }`.
|
|
16
|
+
* - Discriminated unions of object literals (a shared string-literal tag property) are emitted as a JTD discriminator form.
|
|
17
|
+
*
|
|
18
|
+
* @param reflection - The Deepkit type reflection to convert.
|
|
19
|
+
* @returns The corresponding JTD form, or `undefined` if the type cannot be represented.
|
|
17
20
|
*/
|
|
18
|
-
declare function
|
|
21
|
+
declare function reflectionToJsonSchema<TMetadata extends Partial<SchemaMetadata> = Partial<SchemaMetadata>>(reflection: Type): JTDSchemaType<TMetadata> | undefined;
|
|
19
22
|
//#endregion
|
|
20
|
-
export {
|
|
23
|
+
export { reflectionToJsonSchema };
|
|
21
24
|
//# sourceMappingURL=reflection.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reflection.d.cts","names":[],"sources":["../src/reflection.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"reflection.d.cts","names":[],"sources":["../src/reflection.ts"],"mappings":";;;;;;AA2FA;;;;;;;;;;;;;;iBAAgB,sBAAA,mBACI,OAAA,CAAQ,cAAA,IAAkB,OAAA,CAAQ,cAAA,EAAA,CACpD,UAAA,EAAY,IAAA,GAAO,aAAA,CAAc,SAAA"}
|
package/dist/reflection.d.mts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { JTDSchemaType, SchemaMetadata } from "./types.mjs";
|
|
2
|
+
import { Type } from "@powerlines/deepkit/vendor/type";
|
|
3
3
|
|
|
4
4
|
//#region src/reflection.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* Converts a Deepkit type reflection into a JSON
|
|
6
|
+
* Converts a Deepkit type reflection into a JSON Type Definition (RFC 8927) form suitable for AJV's JTD validator.
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
declare function reflectionToJsonSchema(reflection: Type): JsonSchema7Type | undefined;
|
|
12
|
-
/**
|
|
13
|
-
* Builds a JSON Schema object representation from a Deepkit class or object literal type.
|
|
8
|
+
* @remarks
|
|
9
|
+
* Some TypeScript constructs have no direct JTD equivalent and are handled with the closest available form:
|
|
14
10
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* - `null` and `undefined` become the empty JTD form with `nullable: true`.
|
|
12
|
+
* - Unions of primitives that cannot be expressed as a JTD enum collapse to the empty form (which validates any value).
|
|
13
|
+
* - String/number/bigint literal unions are emitted as a JTD enum (non-string members are stringified, as JTD requires string enum members).
|
|
14
|
+
* - Tuples are emitted as a JTD elements form whose element schema is the single tuple member type, or the empty schema for mixed tuples.
|
|
15
|
+
* - `Date` is emitted as `{ type: "timestamp" }`.
|
|
16
|
+
* - Discriminated unions of object literals (a shared string-literal tag property) are emitted as a JTD discriminator form.
|
|
17
|
+
*
|
|
18
|
+
* @param reflection - The Deepkit type reflection to convert.
|
|
19
|
+
* @returns The corresponding JTD form, or `undefined` if the type cannot be represented.
|
|
17
20
|
*/
|
|
18
|
-
declare function
|
|
21
|
+
declare function reflectionToJsonSchema<TMetadata extends Partial<SchemaMetadata> = Partial<SchemaMetadata>>(reflection: Type): JTDSchemaType<TMetadata> | undefined;
|
|
19
22
|
//#endregion
|
|
20
|
-
export {
|
|
23
|
+
export { reflectionToJsonSchema };
|
|
21
24
|
//# sourceMappingURL=reflection.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reflection.d.mts","names":[],"sources":["../src/reflection.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"reflection.d.mts","names":[],"sources":["../src/reflection.ts"],"mappings":";;;;;;AA2FA;;;;;;;;;;;;;;iBAAgB,sBAAA,mBACI,OAAA,CAAQ,cAAA,IAAkB,OAAA,CAAQ,cAAA,EAAA,CACpD,UAAA,EAAY,IAAA,GAAO,aAAA,CAAc,SAAA"}
|