@powerlines/schema 0.11.0 → 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.
Files changed (62) hide show
  1. package/README.md +36 -22
  2. package/dist/bundle.d.cts +2 -2
  3. package/dist/bundle.d.cts.map +1 -1
  4. package/dist/bundle.d.mts +2 -2
  5. package/dist/bundle.d.mts.map +1 -1
  6. package/dist/bundle.mjs.map +1 -1
  7. package/dist/codegen.cjs +27 -0
  8. package/dist/codegen.d.cts +15 -0
  9. package/dist/codegen.d.cts.map +1 -0
  10. package/dist/codegen.d.mts +15 -0
  11. package/dist/codegen.d.mts.map +1 -0
  12. package/dist/codegen.mjs +24 -0
  13. package/dist/codegen.mjs.map +1 -0
  14. package/dist/extract.cjs +201 -52
  15. package/dist/extract.d.cts +81 -16
  16. package/dist/extract.d.cts.map +1 -1
  17. package/dist/extract.d.mts +81 -16
  18. package/dist/extract.d.mts.map +1 -1
  19. package/dist/extract.mjs +199 -55
  20. package/dist/extract.mjs.map +1 -1
  21. package/dist/index.cjs +15 -3
  22. package/dist/index.d.cts +7 -5
  23. package/dist/index.d.mts +7 -5
  24. package/dist/index.mjs +6 -4
  25. package/dist/jtd.cjs +385 -0
  26. package/dist/jtd.d.cts +15 -0
  27. package/dist/jtd.d.cts.map +1 -0
  28. package/dist/jtd.d.mts +15 -0
  29. package/dist/jtd.d.mts.map +1 -0
  30. package/dist/jtd.mjs +384 -0
  31. package/dist/jtd.mjs.map +1 -0
  32. package/dist/reflection.cjs +321 -100
  33. package/dist/reflection.d.cts +16 -13
  34. package/dist/reflection.d.cts.map +1 -1
  35. package/dist/reflection.d.mts +16 -13
  36. package/dist/reflection.d.mts.map +1 -1
  37. package/dist/reflection.mjs +323 -101
  38. package/dist/reflection.mjs.map +1 -1
  39. package/dist/resolve.d.cts +5 -5
  40. package/dist/resolve.d.cts.map +1 -1
  41. package/dist/resolve.d.mts +5 -5
  42. package/dist/resolve.d.mts.map +1 -1
  43. package/dist/resolve.mjs.map +1 -1
  44. package/dist/type-checks.cjs +76 -0
  45. package/dist/type-checks.d.cts +43 -0
  46. package/dist/type-checks.d.cts.map +1 -0
  47. package/dist/type-checks.d.mts +43 -0
  48. package/dist/type-checks.d.mts.map +1 -0
  49. package/dist/type-checks.mjs +71 -0
  50. package/dist/type-checks.mjs.map +1 -0
  51. package/dist/types.d.cts +166 -24
  52. package/dist/types.d.cts.map +1 -1
  53. package/dist/types.d.mts +166 -24
  54. package/dist/types.d.mts.map +1 -1
  55. package/package.json +25 -19
  56. package/dist/is-schema-definition.cjs +0 -18
  57. package/dist/is-schema-definition.d.cts +0 -13
  58. package/dist/is-schema-definition.d.cts.map +0 -1
  59. package/dist/is-schema-definition.d.mts +0 -13
  60. package/dist/is-schema-definition.d.mts.map +0 -1
  61. package/dist/is-schema-definition.mjs +0 -17
  62. package/dist/is-schema-definition.mjs.map +0 -1
@@ -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
- * Converts a Deepkit type reflection into a JSON Schema representation.
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 JSON Schema representation, or `undefined` if the type cannot be represented.
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: return { not: {} };
20
- case _powerlines_deepkit_vendor_type.ReflectionKind.null: return { type: "null" };
21
- case _powerlines_deepkit_vendor_type.ReflectionKind.string: return { type: "string" };
22
- case _powerlines_deepkit_vendor_type.ReflectionKind.boolean: return { type: "boolean" };
23
- case _powerlines_deepkit_vendor_type.ReflectionKind.number: {
24
- const brand = reflection.brand;
25
- return { type: brand !== void 0 && brand >= _powerlines_deepkit_vendor_type.TypeNumberBrand.integer && brand <= _powerlines_deepkit_vendor_type.TypeNumberBrand.uint32 ? "integer" : "number" };
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
- type: "integer",
29
- format: "int64"
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
- type: "string",
36
- const: literal
98
+ ...schema,
99
+ enum: [literal]
37
100
  };
38
- if (typeof literal === "number") return {
39
- type: Number.isInteger(literal) ? "integer" : "number",
40
- const: literal
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
- type: "boolean",
44
- const: literal
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
- type: "string",
52
- pattern: literal.source
110
+ ...schema,
111
+ type: "string"
53
112
  };
54
- return {};
113
+ return schema;
55
114
  }
56
- case _powerlines_deepkit_vendor_type.ReflectionKind.templateLiteral: return { type: "string" };
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 allStrings = values.every((value) => typeof value === "string");
60
- const allNumbers = values.every((value) => typeof value === "number");
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
- type: allStrings ? "string" : allNumbers ? "number" : ["string", "number"],
63
- enum: values
124
+ ...schema,
125
+ enum: unique
64
126
  };
65
127
  }
66
128
  case _powerlines_deepkit_vendor_type.ReflectionKind.array: {
67
- const items = reflectionToJsonSchema(reflection.type);
68
- return items ? {
69
- type: "array",
70
- items
71
- } : { type: "array" };
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 anyOf = reflection.types.map((inner) => reflectionToJsonSchema(inner)).filter((item) => item !== void 0);
81
- if (anyOf.length === 0) return;
82
- if (anyOf.length === 1) return anyOf[0];
83
- return { anyOf };
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 allOf = reflection.types.map((inner) => reflectionToJsonSchema(inner)).filter((item) => item !== void 0);
87
- if (allOf.length === 0) return;
88
- if (allOf.length === 1) return allOf[0];
89
- return { allOf };
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 reflectionToJsonSchema(reflection.type);
92
- case _powerlines_deepkit_vendor_type.ReflectionKind.objectLiteral: return objectReflectionToJsonSchema(reflection);
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
- type: "string",
96
- format: "date-time"
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
- type: "string",
101
- format: "uri"
218
+ ...schema,
219
+ type: "string"
102
220
  };
103
221
  case "Set": {
104
222
  const itemType = reflection.arguments?.[0];
105
- const items = itemType ? reflectionToJsonSchema(itemType) : void 0;
106
- return items ? {
107
- type: "array",
108
- uniqueItems: true,
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 additionalProperties = valueType ? reflectionToJsonSchema(valueType) : void 0;
118
- const schema = { type: "object" };
119
- if (additionalProperties) schema.additionalProperties = additionalProperties;
120
- return schema;
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
- type: "string",
134
- contentEncoding: "base64"
248
+ ...schema,
249
+ type: "string"
135
250
  };
136
251
  case void 0:
137
- default: return objectReflectionToJsonSchema(reflection);
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
- * Builds a JSON Schema object representation from a Deepkit class or object literal type.
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 JSON Schema object describing the type's properties.
364
+ * @returns A JTD properties or values form describing the type's members.
161
365
  */
162
- function objectReflectionToJsonSchema(type) {
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 required = [];
165
- let additionalProperties;
166
- const description = "description" in type && typeof type.description === "string" ? type.description : void 0;
167
- for (const member of type.types) if (member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.property || member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.propertySignature) {
168
- const property = member;
169
- if (typeof property.name !== "string") continue;
170
- const propertySchema = reflectionToJsonSchema(property.type);
171
- if (!propertySchema) continue;
172
- if (typeof property.description === "string") propertySchema.description = property.description;
173
- properties[property.name] = propertySchema;
174
- if (!property.optional) required.push(property.name);
175
- } else if (member.kind === _powerlines_deepkit_vendor_type.ReflectionKind.indexSignature) {
176
- const valueSchema = reflectionToJsonSchema(member.type);
177
- if (valueSchema) additionalProperties = valueSchema;
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
- const schema = {
180
- type: "object",
181
- properties
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;
@@ -1,21 +1,24 @@
1
- import { Type, TypeClass, TypeObjectLiteral } from "@powerlines/deepkit/vendor/type";
2
- import { JsonSchema7Type } from "@stryke/json/types";
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 Schema representation.
6
+ * Converts a Deepkit type reflection into a JSON Type Definition (RFC 8927) form suitable for AJV's JTD validator.
7
7
  *
8
- * @param reflection - The Deepkit type reflection to convert.
9
- * @returns The corresponding JSON Schema representation, or `undefined` if the type cannot be represented.
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
- * @param type - The class or object literal type whose members should be serialized.
16
- * @returns A JSON Schema object describing the type's properties.
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 objectReflectionToJsonSchema(type: TypeObjectLiteral | TypeClass): JsonSchema7Type;
21
+ declare function reflectionToJsonSchema<TMetadata extends Partial<SchemaMetadata> = Partial<SchemaMetadata>>(reflection: Type): JTDSchemaType<TMetadata> | undefined;
19
22
  //#endregion
20
- export { objectReflectionToJsonSchema, reflectionToJsonSchema };
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":";;;;;AAgCA;;;;;iBAAgB,sBAAA,CACd,UAAA,EAAY,IAAA,GACX,eAAA;;;;AAgMH;;;iBAAgB,4BAAA,CACd,IAAA,EAAM,iBAAA,GAAoB,SAAA,GACzB,eAAA"}
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"}
@@ -1,21 +1,24 @@
1
- import { Type, TypeClass, TypeObjectLiteral } from "@powerlines/deepkit/vendor/type";
2
- import { JsonSchema7Type } from "@stryke/json/types";
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 Schema representation.
6
+ * Converts a Deepkit type reflection into a JSON Type Definition (RFC 8927) form suitable for AJV's JTD validator.
7
7
  *
8
- * @param reflection - The Deepkit type reflection to convert.
9
- * @returns The corresponding JSON Schema representation, or `undefined` if the type cannot be represented.
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
- * @param type - The class or object literal type whose members should be serialized.
16
- * @returns A JSON Schema object describing the type's properties.
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 objectReflectionToJsonSchema(type: TypeObjectLiteral | TypeClass): JsonSchema7Type;
21
+ declare function reflectionToJsonSchema<TMetadata extends Partial<SchemaMetadata> = Partial<SchemaMetadata>>(reflection: Type): JTDSchemaType<TMetadata> | undefined;
19
22
  //#endregion
20
- export { objectReflectionToJsonSchema, reflectionToJsonSchema };
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":";;;;;AAgCA;;;;;iBAAgB,sBAAA,CACd,UAAA,EAAY,IAAA,GACX,eAAA;;;;AAgMH;;;iBAAgB,4BAAA,CACd,IAAA,EAAM,iBAAA,GAAoB,SAAA,GACzB,eAAA"}
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"}