@fgv/ts-json-base 5.1.0-33 → 5.1.0-34

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 (38) hide show
  1. package/dist/index.browser.js +2 -1
  2. package/dist/index.browser.js.map +1 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/packlets/json-schema-builder/factories.js +342 -0
  6. package/dist/packlets/json-schema-builder/factories.js.map +1 -0
  7. package/dist/packlets/json-schema-builder/fromJson.js +371 -0
  8. package/dist/packlets/json-schema-builder/fromJson.js.map +1 -0
  9. package/dist/packlets/json-schema-builder/index.js +36 -0
  10. package/dist/packlets/json-schema-builder/index.js.map +1 -0
  11. package/dist/packlets/json-schema-builder/types.js +23 -0
  12. package/dist/packlets/json-schema-builder/types.js.map +1 -0
  13. package/dist/ts-json-base.d.ts +345 -6
  14. package/lib/index.browser.d.ts +2 -1
  15. package/lib/index.browser.d.ts.map +1 -1
  16. package/lib/index.browser.js +3 -1
  17. package/lib/index.browser.js.map +1 -1
  18. package/lib/index.d.ts +2 -1
  19. package/lib/index.d.ts.map +1 -1
  20. package/lib/index.js +3 -1
  21. package/lib/index.js.map +1 -1
  22. package/lib/packlets/json-schema-builder/factories.d.ts +95 -0
  23. package/lib/packlets/json-schema-builder/factories.d.ts.map +1 -0
  24. package/lib/packlets/json-schema-builder/factories.js +352 -0
  25. package/lib/packlets/json-schema-builder/factories.js.map +1 -0
  26. package/lib/packlets/json-schema-builder/fromJson.d.ts +45 -0
  27. package/lib/packlets/json-schema-builder/fromJson.d.ts.map +1 -0
  28. package/lib/packlets/json-schema-builder/fromJson.js +375 -0
  29. package/lib/packlets/json-schema-builder/fromJson.js.map +1 -0
  30. package/lib/packlets/json-schema-builder/index.d.ts +15 -0
  31. package/lib/packlets/json-schema-builder/index.d.ts.map +1 -0
  32. package/lib/packlets/json-schema-builder/index.js +52 -0
  33. package/lib/packlets/json-schema-builder/index.js.map +1 -0
  34. package/lib/packlets/json-schema-builder/types.d.ts +142 -0
  35. package/lib/packlets/json-schema-builder/types.d.ts.map +1 -0
  36. package/lib/packlets/json-schema-builder/types.js +24 -0
  37. package/lib/packlets/json-schema-builder/types.js.map +1 -0
  38. package/package.json +6 -6
@@ -0,0 +1,352 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.string = string;
25
+ exports.number = number;
26
+ exports.integer = integer;
27
+ exports.boolean = boolean;
28
+ exports.enumOf = enumOf;
29
+ exports.optional = optional;
30
+ exports.array = array;
31
+ exports.object = object;
32
+ const ts_utils_1 = require("@fgv/ts-utils");
33
+ // ---------------------------------------------------------------------------
34
+ // Internal helper
35
+ // ---------------------------------------------------------------------------
36
+ // ---------------------------------------------------------------------------
37
+ // Abstract base
38
+ // ---------------------------------------------------------------------------
39
+ /**
40
+ * Abstract base class for all schema validator nodes. Extends GenericValidator so
41
+ * each schema value IS a `Validator<T>` and also carries `toJson()` and `_type`.
42
+ * @internal
43
+ */
44
+ class SchemaValidatorBase extends ts_utils_1.Validation.Base.GenericValidator {
45
+ constructor(type, validator, description) {
46
+ super({ validator });
47
+ this._type = type;
48
+ if (description !== undefined) {
49
+ this.description = description;
50
+ }
51
+ }
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Concrete schema node classes
55
+ // ---------------------------------------------------------------------------
56
+ class StringSchemaValidator extends SchemaValidatorBase {
57
+ constructor(opts) {
58
+ const inner = ts_utils_1.Validators.string;
59
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
60
+ super('string', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
61
+ this._inner = inner;
62
+ }
63
+ validate(from) {
64
+ return this._inner.validate(from);
65
+ }
66
+ convert(from) {
67
+ return this._inner.validate(from);
68
+ }
69
+ toJson() {
70
+ return Object.assign({ type: 'string' }, _descriptionField(this));
71
+ }
72
+ }
73
+ /**
74
+ * Number/integer schema validator.
75
+ *
76
+ * Strict mode (default): delegates to a Validator that only accepts genuine JSON numbers.
77
+ * Non-strict mode: delegates to a Converter that coerces numeric strings (e.g. '42' -\> 42).
78
+ * Both validate() and convert() route through the same underlying logic so that schema nodes
79
+ * behave correctly when used as field validators inside Converters.object / Converters.arrayOf
80
+ * (which call convert()) and when called directly as Validators (which call validate()).
81
+ */
82
+ class NumberSchemaValidator extends SchemaValidatorBase {
83
+ constructor(type, opts) {
84
+ const strict = (opts === null || opts === void 0 ? void 0 : opts.strict) !== false;
85
+ const isInteger = type === 'integer';
86
+ if (strict) {
87
+ const strictValidator = isInteger
88
+ ? ts_utils_1.Validators.isA('integer', (v) => typeof v === 'number' && Number.isInteger(v))
89
+ : ts_utils_1.Validators.isA('number', (v) => typeof v === 'number' && !Number.isNaN(v));
90
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
91
+ super(type, (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
92
+ this._strictValidator = strictValidator;
93
+ this._nonStrictConverter = undefined;
94
+ }
95
+ else {
96
+ // Non-strict: the ValidatorFunc is a placeholder — validate() and convert() are both
97
+ // overridden below to use the coercing Converter, so this lambda is never invoked.
98
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
99
+ super(type, (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
100
+ this._strictValidator = undefined;
101
+ this._nonStrictConverter = isInteger
102
+ ? ts_utils_1.Converters.number.withConstraint((n) => Number.isInteger(n) || (0, ts_utils_1.fail)(`${n}: not an integer`))
103
+ : ts_utils_1.Converters.number;
104
+ }
105
+ this.strict = strict;
106
+ }
107
+ validate(from) {
108
+ if (this._nonStrictConverter !== undefined) {
109
+ // Non-strict path: delegate to the coercing Converter so that '42' -> 42.
110
+ return this._nonStrictConverter.convert(from);
111
+ }
112
+ // Strict path: use the strict in-place validator.
113
+ return this._strictValidator.validate(from);
114
+ }
115
+ convert(from) {
116
+ // Symmetric with validate(): both paths route through the same underlying logic.
117
+ // This ensures correctness when used as a field validator inside Converters.object
118
+ // or Converters.arrayOf (which call convert(), not validate()).
119
+ if (this._nonStrictConverter !== undefined) {
120
+ return this._nonStrictConverter.convert(from);
121
+ }
122
+ return this._strictValidator.validate(from);
123
+ }
124
+ toJson() {
125
+ return Object.assign({ type: this._type }, _descriptionField(this));
126
+ }
127
+ }
128
+ class BooleanSchemaValidator extends SchemaValidatorBase {
129
+ constructor(opts) {
130
+ const inner = ts_utils_1.Validators.boolean;
131
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
132
+ super('boolean', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
133
+ this._inner = inner;
134
+ }
135
+ validate(from) {
136
+ return this._inner.validate(from);
137
+ }
138
+ convert(from) {
139
+ return this._inner.validate(from);
140
+ }
141
+ toJson() {
142
+ return Object.assign({ type: 'boolean' }, _descriptionField(this));
143
+ }
144
+ }
145
+ class EnumSchemaValidator extends SchemaValidatorBase {
146
+ constructor(values, opts) {
147
+ const inner = ts_utils_1.Validators.enumeratedValue(values);
148
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
149
+ super('enum', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
150
+ this.enum = values;
151
+ this._inner = inner;
152
+ }
153
+ validate(from) {
154
+ return this._inner.validate(from);
155
+ }
156
+ convert(from) {
157
+ return this._inner.validate(from);
158
+ }
159
+ toJson() {
160
+ return Object.assign({ type: 'string', enum: [...this.enum] }, _descriptionField(this));
161
+ }
162
+ }
163
+ class OptionalSchemaValidator extends SchemaValidatorBase {
164
+ constructor(inner) {
165
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
166
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
167
+ super('optional', (__from) => true);
168
+ this._schema = inner;
169
+ }
170
+ validate(from) {
171
+ // Route through convert() so that inner transformations (e.g. non-strict number coercion,
172
+ // object property stripping) are applied even when validate() is called directly.
173
+ return this.convert(from);
174
+ }
175
+ convert(from) {
176
+ if (from === undefined) {
177
+ return (0, ts_utils_1.succeed)(undefined);
178
+ }
179
+ // Delegate to inner.convert() so that coercions and transformations from the inner schema
180
+ // propagate to callers (both Converters.object field evaluation and direct validate() calls).
181
+ return this._schema.convert(from);
182
+ }
183
+ toJson() {
184
+ // optionality is transparent at the JSON Schema level; the parent emits required/optional split.
185
+ return this._schema.toJson();
186
+ }
187
+ }
188
+ class ArraySchemaValidator extends SchemaValidatorBase {
189
+ constructor(items, opts) {
190
+ // Converters.arrayOf calls .convert() on each element, which routes through the item schema's
191
+ // convert() override — correctly propagating coercions (e.g. '42' -> 42 for non-strict numbers).
192
+ const converter = ts_utils_1.Converters.arrayOf(items);
193
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
194
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
195
+ super('array', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
196
+ this._items = items;
197
+ this._converter = converter;
198
+ }
199
+ validate(from) {
200
+ // Route through convert() so that element coercions from the item schema propagate.
201
+ return this._converter.convert(from);
202
+ }
203
+ convert(from) {
204
+ return this._converter.convert(from);
205
+ }
206
+ toJson() {
207
+ return Object.assign({ type: 'array', items: this._items.toJson() }, _descriptionField(this));
208
+ }
209
+ }
210
+ class ObjectSchemaValidator extends SchemaValidatorBase {
211
+ constructor(properties, opts) {
212
+ const additionalProperties = (opts === null || opts === void 0 ? void 0 : opts.additionalProperties) === true;
213
+ const converter = _buildObjectConverter(properties, additionalProperties);
214
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
215
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
216
+ super('object', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
217
+ this._properties = properties;
218
+ this.additionalProperties = additionalProperties;
219
+ this._converter = converter;
220
+ }
221
+ validate(from) {
222
+ return this._converter.convert(from);
223
+ }
224
+ convert(from) {
225
+ return this._converter.convert(from);
226
+ }
227
+ toJson() {
228
+ const properties = {};
229
+ const required = [];
230
+ for (const [key, prop] of Object.entries(this._properties)) {
231
+ // OptionalSchemaValidator.toJson() delegates to the inner schema, so optionality is
232
+ // transparent at the wire level and managed via the `required` array.
233
+ properties[key] = prop.toJson();
234
+ if (prop._type !== 'optional') {
235
+ required.push(key);
236
+ }
237
+ }
238
+ return Object.assign(Object.assign(Object.assign({ type: 'object', properties }, (required.length > 0 && { required })), (!this.additionalProperties && { additionalProperties: false })), _descriptionField(this));
239
+ }
240
+ }
241
+ // ---------------------------------------------------------------------------
242
+ // Private builder helpers
243
+ // ---------------------------------------------------------------------------
244
+ /**
245
+ * Builds a `Converter<ObjectStatic<P>>` for object nodes using `Converters.object`.
246
+ * Uses a Converter (not Validator) so that extra properties are stripped from the result —
247
+ * Validators are in-place and would pass unrecognized fields through unchanged.
248
+ * Optional properties are tracked and listed in `optionalFields`.
249
+ * Each property's ISchemaValidator is used as the field converter — its convert() method
250
+ * is called by ObjectConverter for each field, propagating nested coercions and transforms.
251
+ */
252
+ function _buildObjectConverter(properties, additionalProperties) {
253
+ const fields = {};
254
+ const optionalKeys = [];
255
+ for (const [key, prop] of Object.entries(properties)) {
256
+ // Each prop IS both a Validator<unknown> and a Converter<unknown> (ISchemaValidator extends both,
257
+ // and all schema classes override convert()). We cast to Validator<unknown> for the field-map
258
+ // construction; ObjectConverter calls .convert() on each field, which routes through the schema's
259
+ // convert() override and propagates nested coercions correctly.
260
+ fields[key] = prop;
261
+ if (prop._type === 'optional') {
262
+ optionalKeys.push(key);
263
+ }
264
+ }
265
+ return ts_utils_1.Converters.object(fields, {
266
+ optionalFields: optionalKeys,
267
+ strict: !additionalProperties
268
+ });
269
+ }
270
+ function _descriptionField(schema) {
271
+ return schema.description !== undefined ? { description: schema.description } : {};
272
+ }
273
+ // ---------------------------------------------------------------------------
274
+ // Public factory functions
275
+ // ---------------------------------------------------------------------------
276
+ /**
277
+ * Creates a schema node for a JSON `string`.
278
+ * @param opts - Optional description.
279
+ * @returns An `ISchemaValidator` whose `Static` type is `string`.
280
+ * @public
281
+ */
282
+ function string(opts) {
283
+ return new StringSchemaValidator(opts);
284
+ }
285
+ /**
286
+ * Creates a schema node for a JSON `number`.
287
+ * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).
288
+ * @returns An `ISchemaValidator` whose `Static` type is `number`.
289
+ * @public
290
+ */
291
+ function number(opts) {
292
+ return new NumberSchemaValidator('number', opts);
293
+ }
294
+ /**
295
+ * Creates a schema node for a JSON `integer`.
296
+ * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).
297
+ * @returns An `ISchemaValidator` (tagged `integer`) whose `Static` type is `number`.
298
+ * @public
299
+ */
300
+ function integer(opts) {
301
+ return new NumberSchemaValidator('integer', opts);
302
+ }
303
+ /**
304
+ * Creates a schema node for a JSON `boolean`.
305
+ * @param opts - Optional description.
306
+ * @returns An `ISchemaValidator` whose `Static` type is `boolean`.
307
+ * @public
308
+ */
309
+ function boolean(opts) {
310
+ return new BooleanSchemaValidator(opts);
311
+ }
312
+ /**
313
+ * Creates a schema node for a closed set of string literals.
314
+ * @param values - The allowed string values.
315
+ * @param opts - Optional description.
316
+ * @returns An `ISchemaValidator` whose `Static` type is the union of `values`.
317
+ * @public
318
+ */
319
+ function enumOf(values, opts) {
320
+ return new EnumSchemaValidator(values, opts);
321
+ }
322
+ /**
323
+ * Marks a property schema as optional within an `object` schema.
324
+ * @param schema - The inner schema to make optional.
325
+ * @returns An `ISchemaValidator` whose `Static` type is `Static<S> | undefined`.
326
+ * @public
327
+ */
328
+ function optional(schema) {
329
+ return new OptionalSchemaValidator(schema);
330
+ }
331
+ /**
332
+ * Creates a schema node for a JSON `array` whose elements all match `items`.
333
+ * @param items - The schema applied to every element.
334
+ * @param opts - Optional description.
335
+ * @returns An `ISchemaValidator` whose `Static` type is `Array<Static<S>>`.
336
+ * @public
337
+ */
338
+ function array(items, opts) {
339
+ return new ArraySchemaValidator(items, opts);
340
+ }
341
+ /**
342
+ * Creates a schema node for a JSON `object` with a fixed set of typed properties.
343
+ * @param properties - A record mapping property names to their schemas. Wrap a property with
344
+ * `optional` to make it optional.
345
+ * @param opts - Optional description and `additionalProperties` flag.
346
+ * @returns An `ISchemaValidator` whose `Static` type derives the required/optional split.
347
+ * @public
348
+ */
349
+ function object(properties, opts) {
350
+ return new ObjectSchemaValidator(properties, opts);
351
+ }
352
+ //# sourceMappingURL=factories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.js","sourceRoot":"","sources":["../../../src/packlets/json-schema-builder/factories.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;AA0YH,wBAEC;AAQD,wBAEC;AAQD,0BAEC;AAQD,0BAEC;AASD,wBAEC;AAQD,4BAIC;AASD,sBAKC;AAUD,wBAKC;AA5dD,4CAUuB;AAwCvB,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;GAIG;AACH,MAAe,mBACb,SAAQ,qBAAU,CAAC,IAAI,CAAC,gBAAmB;IAO3C,YACE,IAAoB,EACpB,SAA+C,EAC/C,WAAoB;QAEpB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QACjC,CAAC;IACH,CAAC;CAGF;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,MAAM,qBAAsB,SAAQ,mBAA2B;IAG7D,YAAmB,IAAqB;QACtC,MAAM,KAAK,GAAG,qBAAU,CAAC,MAAM,CAAC;QAChC,uGAAuG;QACvG,KAAK,CAAC,QAAQ,EAAE,CAAC,MAAe,EAA6B,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEM,MAAM;QACX,uBAAS,IAAI,EAAE,QAAQ,IAAK,iBAAiB,CAAC,IAAI,CAAC,EAAG;IACxD,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,qBAAsB,SAAQ,mBAA2B;IAO7D,YAAmB,IAA0B,EAAE,IAA2B;QACxE,MAAM,MAAM,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,MAAM,MAAK,KAAK,CAAC;QACtC,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,CAAC;QAErC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,eAAe,GAAG,SAAS;gBAC/B,CAAC,CAAC,qBAAU,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC7F,CAAC,CAAC,qBAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,uGAAuG;YACvG,KAAK,CAAC,IAAI,EAAE,CAAC,MAAe,EAA6B,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;YACrF,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;YACxC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,qFAAqF;YACrF,mFAAmF;YACnF,uGAAuG;YACvG,KAAK,CAAC,IAAI,EAAE,CAAC,MAAe,EAA6B,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;YACrF,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAClC,IAAI,CAAC,mBAAmB,GAAG,SAAS;gBAClC,CAAC,CAAC,qBAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAA,eAAI,EAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAC9F,CAAC,CAAC,qBAAU,CAAC,MAAM,CAAC;QACxB,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC3C,0EAA0E;YAC1E,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QACD,kDAAkD;QAClD,OAAO,IAAI,CAAC,gBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,iFAAiF;QACjF,mFAAmF;QACnF,gEAAgE;QAChE,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,gBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAEM,MAAM;QACX,uBAAS,IAAI,EAAE,IAAI,CAAC,KAAK,IAAK,iBAAiB,CAAC,IAAI,CAAC,EAAG;IAC1D,CAAC;CACF;AAED,MAAM,sBAAuB,SAAQ,mBAA4B;IAG/D,YAAmB,IAAqB;QACtC,MAAM,KAAK,GAAG,qBAAU,CAAC,OAAO,CAAC;QACjC,uGAAuG;QACvG,KAAK,CAAC,SAAS,EAAE,CAAC,MAAe,EAA8B,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;QAC3F,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEM,MAAM;QACX,uBAAS,IAAI,EAAE,SAAS,IAAK,iBAAiB,CAAC,IAAI,CAAC,EAAG;IACzD,CAAC;CACF;AAED,MAAM,mBAAsC,SAAQ,mBAAsB;IAIxE,YAAmB,MAAwB,EAAE,IAAqB;QAChE,MAAM,KAAK,GAAG,qBAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACjD,uGAAuG;QACvG,KAAK,CAAC,MAAM,EAAE,CAAC,MAAe,EAAwB,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;QAClF,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEM,MAAM;QACX,uBAAS,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAK,iBAAiB,CAAC,IAAI,CAAC,EAAG;IAC9E,CAAC;CACF;AAED,MAAM,uBAA6D,SAAQ,mBAE1E;IAGC,YAAmB,KAAQ;QACzB,kFAAkF;QAClF,uGAAuG;QACvG,KAAK,CAAC,UAAU,EAAE,CAAC,MAAe,EAA4C,EAAE,CAAC,IAAI,CAAC,CAAC;QACvF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,0FAA0F;QAC1F,kFAAkF;QAClF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,IAAA,kBAAO,EAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QACD,0FAA0F;QAC1F,8FAA8F;QAC9F,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAkC,CAAC;IACrE,CAAC;IAEM,MAAM;QACX,iGAAiG;QACjG,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,oBAA0D,SAAQ,mBAAgC;IAMtG,YAAmB,KAAQ,EAAE,IAAqB;QAChD,8FAA8F;QAC9F,iGAAiG;QACjG,MAAM,SAAS,GAAG,qBAAU,CAAC,OAAO,CAAC,KAAwC,CAAC,CAAC;QAC/E,kFAAkF;QAClF,uGAAuG;QACvG,KAAK,CAAC,OAAO,EAAE,CAAC,MAAe,EAAkC,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;QAC7F,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,oFAAoF;QACpF,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAEM,MAAM;QACX,uBAAS,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAK,iBAAiB,CAAC,IAAI,CAAC,EAAG;IACpF,CAAC;CACF;AAED,MAAM,qBAAgD,SAAQ,mBAAoC;IAShG,YAAmB,UAAa,EAAE,IAA2B;QAC3D,MAAM,oBAAoB,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,oBAAoB,MAAK,IAAI,CAAC;QACjE,MAAM,SAAS,GAAG,qBAAqB,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;QAC1E,kFAAkF;QAClF,uGAAuG;QACvG,KAAK,CAAC,QAAQ,EAAE,CAAC,MAAe,EAAsC,EAAE,CAAC,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,CAAC;QAClG,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QACjD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAEe,QAAQ,CAAC,IAAa;QACpC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAEe,OAAO,CAAC,IAAa;QACnC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAEM,MAAM;QACX,MAAM,UAAU,GAAe,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3D,oFAAoF;YACpF,sEAAsE;YACtE,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAC9B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QAED,mDACE,IAAI,EAAE,QAAQ,EACd,UAAU,IACP,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,GACrC,CAAC,CAAC,IAAI,CAAC,oBAAoB,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC,GAC/D,iBAAiB,CAAC,IAAI,CAAC,EAC1B;IACJ,CAAC;CACF;AAED,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAC5B,UAAa,EACb,oBAA6B;IAE7B,MAAM,MAAM,GAAuE,EAAE,CAAC;IACtF,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,kGAAkG;QAClG,8FAA8F;QAC9F,kGAAkG;QAClG,gEAAgE;QAChE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAgD,CAAC;QAC/D,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC9B,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,qBAAU,CAAC,MAAM,CAAC,MAAqD,EAAE;QAC9E,cAAc,EAAE,YAAyC;QACzD,MAAM,EAAE,CAAC,oBAAoB;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAiC;IAC1D,OAAO,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;GAKG;AACH,SAAgB,MAAM,CAAC,IAAqB;IAC1C,OAAO,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,MAAM,CAAC,IAA2B;IAChD,OAAO,IAAI,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,OAAO,CAAC,IAA2B;IACjD,OAAO,IAAI,qBAAqB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,OAAO,CAAC,IAAqB;IAC3C,OAAO,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,MAAM,CAAmB,MAAoB,EAAE,IAAqB;IAClF,OAAO,IAAI,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CACtB,MAAS;IAET,OAAO,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,KAAK,CACnB,KAAQ,EACR,IAAqB;IAErB,OAAO,IAAI,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,MAAM,CACpB,UAAa,EACb,IAA2B;IAE3B,OAAO,IAAI,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport {\n Conversion,\n Converter,\n Converters,\n Failure,\n Result,\n Validation,\n Validators,\n fail,\n succeed\n} from '@fgv/ts-utils';\nimport { JsonObject } from '../json';\nimport { ILlmProperties, ISchemaValidator, ObjectStatic, SchemaNodeType, Static } from './types';\n\n/**\n * Common options accepted by every schema factory.\n * @public\n */\nexport interface ISchemaOptions {\n /**\n * Optional human-readable description emitted into the wire JSON Schema.\n */\n description?: string;\n}\n\n/**\n * Options for the `number` and `integer` factories.\n * @public\n */\nexport interface INumberSchemaOptions extends ISchemaOptions {\n /**\n * When `true` (default), the derived validator rejects numeric strings such as `'42'` and\n * accepts only genuine JSON numbers. Set `false` to opt into permissive coercion (numeric\n * strings are accepted and converted to their numeric value).\n */\n strict?: boolean;\n}\n\n/**\n * Options for the `object` factory.\n * @public\n */\nexport interface IObjectSchemaOptions extends ISchemaOptions {\n /**\n * When `false` (default), the validator rejects unrecognized properties and the emitted schema\n * sets `additionalProperties: false`. Set `true` to allow extra fields.\n */\n additionalProperties?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helper\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Abstract base\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract base class for all schema validator nodes. Extends GenericValidator so\n * each schema value IS a `Validator<T>` and also carries `toJson()` and `_type`.\n * @internal\n */\nabstract class SchemaValidatorBase<T>\n extends Validation.Base.GenericValidator<T>\n implements ISchemaValidator<T>\n{\n public readonly __staticType?: T;\n public readonly _type: SchemaNodeType;\n public readonly description?: string;\n\n protected constructor(\n type: SchemaNodeType,\n validator: Validation.ValidatorFunc<T, unknown>,\n description?: string\n ) {\n super({ validator });\n this._type = type;\n if (description !== undefined) {\n this.description = description;\n }\n }\n\n public abstract toJson(): JsonObject;\n}\n\n// ---------------------------------------------------------------------------\n// Concrete schema node classes\n// ---------------------------------------------------------------------------\n\nclass StringSchemaValidator extends SchemaValidatorBase<string> {\n private readonly _inner: Validation.Validator<string>;\n\n public constructor(opts?: ISchemaOptions) {\n const inner = Validators.string;\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('string', (__from: unknown): boolean | Failure<string> => true, opts?.description);\n this._inner = inner;\n }\n\n public override validate(from: unknown): Result<string> {\n return this._inner.validate(from);\n }\n\n public override convert(from: unknown): Result<string> {\n return this._inner.validate(from);\n }\n\n public toJson(): JsonObject {\n return { type: 'string', ..._descriptionField(this) };\n }\n}\n\n/**\n * Number/integer schema validator.\n *\n * Strict mode (default): delegates to a Validator that only accepts genuine JSON numbers.\n * Non-strict mode: delegates to a Converter that coerces numeric strings (e.g. '42' -\\> 42).\n * Both validate() and convert() route through the same underlying logic so that schema nodes\n * behave correctly when used as field validators inside Converters.object / Converters.arrayOf\n * (which call convert()) and when called directly as Validators (which call validate()).\n */\nclass NumberSchemaValidator extends SchemaValidatorBase<number> {\n public readonly strict: boolean;\n // Held for non-strict mode where coercion is needed.\n private readonly _nonStrictConverter?: Converter<number>;\n // Held for strict mode where in-place validation is the canonical operation.\n private readonly _strictValidator?: Validation.Validator<number>;\n\n public constructor(type: 'number' | 'integer', opts?: INumberSchemaOptions) {\n const strict = opts?.strict !== false;\n const isInteger = type === 'integer';\n\n if (strict) {\n const strictValidator = isInteger\n ? Validators.isA('integer', (v): v is number => typeof v === 'number' && Number.isInteger(v))\n : Validators.isA('number', (v): v is number => typeof v === 'number' && !Number.isNaN(v));\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super(type, (__from: unknown): boolean | Failure<number> => true, opts?.description);\n this._strictValidator = strictValidator;\n this._nonStrictConverter = undefined;\n } else {\n // Non-strict: the ValidatorFunc is a placeholder — validate() and convert() are both\n // overridden below to use the coercing Converter, so this lambda is never invoked.\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super(type, (__from: unknown): boolean | Failure<number> => true, opts?.description);\n this._strictValidator = undefined;\n this._nonStrictConverter = isInteger\n ? Converters.number.withConstraint((n) => Number.isInteger(n) || fail(`${n}: not an integer`))\n : Converters.number;\n }\n\n this.strict = strict;\n }\n\n public override validate(from: unknown): Result<number> {\n if (this._nonStrictConverter !== undefined) {\n // Non-strict path: delegate to the coercing Converter so that '42' -> 42.\n return this._nonStrictConverter.convert(from);\n }\n // Strict path: use the strict in-place validator.\n return this._strictValidator!.validate(from);\n }\n\n public override convert(from: unknown): Result<number> {\n // Symmetric with validate(): both paths route through the same underlying logic.\n // This ensures correctness when used as a field validator inside Converters.object\n // or Converters.arrayOf (which call convert(), not validate()).\n if (this._nonStrictConverter !== undefined) {\n return this._nonStrictConverter.convert(from);\n }\n return this._strictValidator!.validate(from);\n }\n\n public toJson(): JsonObject {\n return { type: this._type, ..._descriptionField(this) };\n }\n}\n\nclass BooleanSchemaValidator extends SchemaValidatorBase<boolean> {\n private readonly _inner: Validation.Validator<boolean>;\n\n public constructor(opts?: ISchemaOptions) {\n const inner = Validators.boolean;\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('boolean', (__from: unknown): boolean | Failure<boolean> => true, opts?.description);\n this._inner = inner;\n }\n\n public override validate(from: unknown): Result<boolean> {\n return this._inner.validate(from);\n }\n\n public override convert(from: unknown): Result<boolean> {\n return this._inner.validate(from);\n }\n\n public toJson(): JsonObject {\n return { type: 'boolean', ..._descriptionField(this) };\n }\n}\n\nclass EnumSchemaValidator<T extends string> extends SchemaValidatorBase<T> {\n public readonly enum: ReadonlyArray<T>;\n private readonly _inner: Validation.Validator<T>;\n\n public constructor(values: ReadonlyArray<T>, opts?: ISchemaOptions) {\n const inner = Validators.enumeratedValue(values);\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('enum', (__from: unknown): boolean | Failure<T> => true, opts?.description);\n this.enum = values;\n this._inner = inner;\n }\n\n public override validate(from: unknown): Result<T> {\n return this._inner.validate(from);\n }\n\n public override convert(from: unknown): Result<T> {\n return this._inner.validate(from);\n }\n\n public toJson(): JsonObject {\n return { type: 'string', enum: [...this.enum], ..._descriptionField(this) };\n }\n}\n\nclass OptionalSchemaValidator<S extends ISchemaValidator<unknown>> extends SchemaValidatorBase<\n Static<S> | undefined\n> {\n public readonly _schema: S;\n\n public constructor(inner: S) {\n // ValidatorFunc placeholder — validate() and convert() are both overridden below.\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('optional', (__from: unknown): boolean | Failure<Static<S> | undefined> => true);\n this._schema = inner;\n }\n\n public override validate(from: unknown): Result<Static<S> | undefined> {\n // Route through convert() so that inner transformations (e.g. non-strict number coercion,\n // object property stripping) are applied even when validate() is called directly.\n return this.convert(from);\n }\n\n public override convert(from: unknown): Result<Static<S> | undefined> {\n if (from === undefined) {\n return succeed(undefined);\n }\n // Delegate to inner.convert() so that coercions and transformations from the inner schema\n // propagate to callers (both Converters.object field evaluation and direct validate() calls).\n return this._schema.convert(from) as Result<Static<S> | undefined>;\n }\n\n public toJson(): JsonObject {\n // optionality is transparent at the JSON Schema level; the parent emits required/optional split.\n return this._schema.toJson();\n }\n}\n\nclass ArraySchemaValidator<S extends ISchemaValidator<unknown>> extends SchemaValidatorBase<Static<S>[]> {\n public readonly _items: S;\n // Uses a Converter (not Validator) so that element convert() is called on each item,\n // propagating coercions from the item schema (e.g. non-strict number coercion).\n private readonly _converter: Converter<Static<S>[]>;\n\n public constructor(items: S, opts?: ISchemaOptions) {\n // Converters.arrayOf calls .convert() on each element, which routes through the item schema's\n // convert() override — correctly propagating coercions (e.g. '42' -> 42 for non-strict numbers).\n const converter = Converters.arrayOf(items as unknown as Converter<Static<S>>);\n // ValidatorFunc placeholder — validate() and convert() are both overridden below.\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('array', (__from: unknown): boolean | Failure<Static<S>[]> => true, opts?.description);\n this._items = items;\n this._converter = converter;\n }\n\n public override validate(from: unknown): Result<Static<S>[]> {\n // Route through convert() so that element coercions from the item schema propagate.\n return this._converter.convert(from);\n }\n\n public override convert(from: unknown): Result<Static<S>[]> {\n return this._converter.convert(from);\n }\n\n public toJson(): JsonObject {\n return { type: 'array', items: this._items.toJson(), ..._descriptionField(this) };\n }\n}\n\nclass ObjectSchemaValidator<P extends ILlmProperties> extends SchemaValidatorBase<ObjectStatic<P>> {\n public readonly _properties: P;\n public readonly additionalProperties: boolean;\n // Uses a Converter (not Validator) so that extra properties are stripped from the result\n // (Validators are in-place and would return the full input object including unknown fields).\n // Both validate() and convert() route through this converter so that nested schema coercions\n // propagate correctly regardless of which method is called.\n private readonly _converter: Converter<ObjectStatic<P>>;\n\n public constructor(properties: P, opts?: IObjectSchemaOptions) {\n const additionalProperties = opts?.additionalProperties === true;\n const converter = _buildObjectConverter(properties, additionalProperties);\n // ValidatorFunc placeholder — validate() and convert() are both overridden below.\n /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */\n super('object', (__from: unknown): boolean | Failure<ObjectStatic<P>> => true, opts?.description);\n this._properties = properties;\n this.additionalProperties = additionalProperties;\n this._converter = converter;\n }\n\n public override validate(from: unknown): Result<ObjectStatic<P>> {\n return this._converter.convert(from);\n }\n\n public override convert(from: unknown): Result<ObjectStatic<P>> {\n return this._converter.convert(from);\n }\n\n public toJson(): JsonObject {\n const properties: JsonObject = {};\n const required: string[] = [];\n\n for (const [key, prop] of Object.entries(this._properties)) {\n // OptionalSchemaValidator.toJson() delegates to the inner schema, so optionality is\n // transparent at the wire level and managed via the `required` array.\n properties[key] = prop.toJson();\n if (prop._type !== 'optional') {\n required.push(key);\n }\n }\n\n return {\n type: 'object',\n properties,\n ...(required.length > 0 && { required }),\n ...(!this.additionalProperties && { additionalProperties: false }),\n ..._descriptionField(this)\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Private builder helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a `Converter<ObjectStatic<P>>` for object nodes using `Converters.object`.\n * Uses a Converter (not Validator) so that extra properties are stripped from the result —\n * Validators are in-place and would pass unrecognized fields through unchanged.\n * Optional properties are tracked and listed in `optionalFields`.\n * Each property's ISchemaValidator is used as the field converter — its convert() method\n * is called by ObjectConverter for each field, propagating nested coercions and transforms.\n */\nfunction _buildObjectConverter<P extends ILlmProperties>(\n properties: P,\n additionalProperties: boolean\n): Converter<ObjectStatic<P>> {\n const fields: Record<string, Converter<unknown> | Validation.Validator<unknown>> = {};\n const optionalKeys: string[] = [];\n\n for (const [key, prop] of Object.entries(properties)) {\n // Each prop IS both a Validator<unknown> and a Converter<unknown> (ISchemaValidator extends both,\n // and all schema classes override convert()). We cast to Validator<unknown> for the field-map\n // construction; ObjectConverter calls .convert() on each field, which routes through the schema's\n // convert() override and propagates nested coercions correctly.\n fields[key] = prop as unknown as Validation.Validator<unknown>;\n if (prop._type === 'optional') {\n optionalKeys.push(key);\n }\n }\n\n return Converters.object(fields as Conversion.FieldConverters<ObjectStatic<P>>, {\n optionalFields: optionalKeys as (keyof ObjectStatic<P>)[],\n strict: !additionalProperties\n });\n}\n\nfunction _descriptionField(schema: ISchemaValidator<unknown>): { description?: string } {\n return schema.description !== undefined ? { description: schema.description } : {};\n}\n\n// ---------------------------------------------------------------------------\n// Public factory functions\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a schema node for a JSON `string`.\n * @param opts - Optional description.\n * @returns An `ISchemaValidator` whose `Static` type is `string`.\n * @public\n */\nexport function string(opts?: ISchemaOptions): ISchemaValidator<string> {\n return new StringSchemaValidator(opts);\n}\n\n/**\n * Creates a schema node for a JSON `number`.\n * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).\n * @returns An `ISchemaValidator` whose `Static` type is `number`.\n * @public\n */\nexport function number(opts?: INumberSchemaOptions): ISchemaValidator<number> {\n return new NumberSchemaValidator('number', opts);\n}\n\n/**\n * Creates a schema node for a JSON `integer`.\n * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).\n * @returns An `ISchemaValidator` (tagged `integer`) whose `Static` type is `number`.\n * @public\n */\nexport function integer(opts?: INumberSchemaOptions): ISchemaValidator<number> {\n return new NumberSchemaValidator('integer', opts);\n}\n\n/**\n * Creates a schema node for a JSON `boolean`.\n * @param opts - Optional description.\n * @returns An `ISchemaValidator` whose `Static` type is `boolean`.\n * @public\n */\nexport function boolean(opts?: ISchemaOptions): ISchemaValidator<boolean> {\n return new BooleanSchemaValidator(opts);\n}\n\n/**\n * Creates a schema node for a closed set of string literals.\n * @param values - The allowed string values.\n * @param opts - Optional description.\n * @returns An `ISchemaValidator` whose `Static` type is the union of `values`.\n * @public\n */\nexport function enumOf<T extends string>(values: readonly T[], opts?: ISchemaOptions): ISchemaValidator<T> {\n return new EnumSchemaValidator(values, opts);\n}\n\n/**\n * Marks a property schema as optional within an `object` schema.\n * @param schema - The inner schema to make optional.\n * @returns An `ISchemaValidator` whose `Static` type is `Static<S> | undefined`.\n * @public\n */\nexport function optional<S extends ISchemaValidator<unknown>>(\n schema: S\n): ISchemaValidator<Static<S> | undefined> {\n return new OptionalSchemaValidator(schema);\n}\n\n/**\n * Creates a schema node for a JSON `array` whose elements all match `items`.\n * @param items - The schema applied to every element.\n * @param opts - Optional description.\n * @returns An `ISchemaValidator` whose `Static` type is `Array<Static<S>>`.\n * @public\n */\nexport function array<S extends ISchemaValidator<unknown>>(\n items: S,\n opts?: ISchemaOptions\n): ISchemaValidator<Static<S>[]> {\n return new ArraySchemaValidator(items, opts);\n}\n\n/**\n * Creates a schema node for a JSON `object` with a fixed set of typed properties.\n * @param properties - A record mapping property names to their schemas. Wrap a property with\n * `optional` to make it optional.\n * @param opts - Optional description and `additionalProperties` flag.\n * @returns An `ISchemaValidator` whose `Static` type derives the required/optional split.\n * @public\n */\nexport function object<P extends ILlmProperties>(\n properties: P,\n opts?: IObjectSchemaOptions\n): ISchemaValidator<ObjectStatic<P>> {\n return new ObjectSchemaValidator(properties, opts);\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { Converter, Result } from '@fgv/ts-utils';
2
+ import { JsonObject, JsonValue } from '../json';
3
+ import { ISchemaValidator } from './types';
4
+ /**
5
+ * The main converter. Parses a raw JSON Schema object into a typed schema validator
6
+ * for the LLM-tool subset.
7
+ *
8
+ * @remarks
9
+ * Performs pre-flight checks (non-object root, union type arrays, forbidden keywords,
10
+ * unknown types) before dispatching to the per-type arm converters.
11
+ *
12
+ * The conversion context (`TC = string`) carries the current JSON Pointer path so that
13
+ * error messages from nested nodes name the actual failing node (e.g.
14
+ * `#/properties/config/properties/inner: 'required' key '...'`) rather than always
15
+ * reporting `#:`. The context defaults to `'#'` when absent (top-level call).
16
+ *
17
+ * Array and object arms reference `jsonSchemaConverter` by name from inside function
18
+ * declarations (hoisted). By the time any arm is called at runtime, `jsonSchemaConverter`
19
+ * is fully initialized, so recursive sub-schema calls also go through the pre-flight checks
20
+ * and produce meaningful error messages.
21
+ *
22
+ * @public
23
+ */
24
+ export declare const jsonSchemaConverter: Converter<ISchemaValidator<JsonValue>, string>;
25
+ /**
26
+ * Parses a raw JSON Schema object (e.g. one discovered at an MCP tool boundary) into a typed schema
27
+ * value within the LLM-tool subset.
28
+ *
29
+ * @remarks
30
+ * Because the static type cannot be recovered from a runtime value, the result is typed as the
31
+ * opaque supertype `ISchemaValidator<JsonValue>` — the honest type when a schema arrives at runtime,
32
+ * since schemas may validate strings, numbers, booleans, arrays, or objects. The `validate()` method
33
+ * performs real runtime validation; the derived static type is the opaque `JsonValue`.
34
+ *
35
+ * Consumers who need a narrower derived type must author the schema via the factories.
36
+ *
37
+ * Out-of-subset features fail loudly (see `FORBIDDEN_KEYWORDS`); `description` is preserved on every
38
+ * node; other annotations (`title`, `default`, `format`, `examples`) are silently ignored.
39
+ *
40
+ * @param json - The raw JSON Schema object to parse.
41
+ * @returns `Success` with the parsed schema, or `Failure` describing the first out-of-subset feature.
42
+ * @public
43
+ */
44
+ export declare function fromJson(json: JsonObject): Result<ISchemaValidator<JsonValue>>;
45
+ //# sourceMappingURL=fromJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fromJson.d.ts","sourceRoot":"","sources":["../../../src/packlets/json-schema-builder/fromJson.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,SAAS,EAAc,MAAM,EAA6B,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAkB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4X3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,CAuC9E,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAE9E"}