@fgv/ts-json-base 5.1.0-4 → 5.1.0-40

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 (146) hide show
  1. package/dist/index.browser.js +2 -1
  2. package/dist/index.browser.js.map +1 -0
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -0
  5. package/dist/packlets/converters/converters.js +11 -0
  6. package/dist/packlets/converters/converters.js.map +1 -0
  7. package/dist/packlets/converters/index.js.map +1 -0
  8. package/dist/packlets/file-tree/directoryItem.js.map +1 -0
  9. package/dist/packlets/file-tree/fileItem.js +11 -4
  10. package/dist/packlets/file-tree/fileItem.js.map +1 -0
  11. package/dist/packlets/file-tree/fileTree.js.map +1 -0
  12. package/dist/packlets/file-tree/fileTreeAccessors.js.map +1 -0
  13. package/dist/packlets/file-tree/fileTreeHelpers.inMemory.js.map +1 -0
  14. package/dist/packlets/file-tree/fileTreeHelpers.js.map +1 -0
  15. package/dist/packlets/file-tree/filterSpec.js.map +1 -0
  16. package/dist/packlets/file-tree/fsTree.js +44 -13
  17. package/dist/packlets/file-tree/fsTree.js.map +1 -0
  18. package/dist/packlets/file-tree/in-memory/inMemoryTree.js +44 -13
  19. package/dist/packlets/file-tree/in-memory/inMemoryTree.js.map +1 -0
  20. package/dist/packlets/file-tree/in-memory/index.js.map +1 -0
  21. package/dist/packlets/file-tree/in-memory/treeBuilder.js.map +1 -0
  22. package/dist/packlets/file-tree/index.browser.js.map +1 -0
  23. package/dist/packlets/file-tree/index.js.map +1 -0
  24. package/dist/packlets/json/common.js.map +1 -0
  25. package/dist/packlets/json/index.js.map +1 -0
  26. package/dist/packlets/json-compatible/common.js.map +1 -0
  27. package/dist/packlets/json-compatible/converters.js.map +1 -0
  28. package/dist/packlets/json-compatible/index.js.map +1 -0
  29. package/dist/packlets/json-compatible/validators.js.map +1 -0
  30. package/dist/packlets/json-file/file.js.map +1 -0
  31. package/dist/packlets/json-file/index.browser.js.map +1 -0
  32. package/dist/packlets/json-file/index.js.map +1 -0
  33. package/dist/packlets/json-file/jsonFsHelper.js.map +1 -0
  34. package/dist/packlets/json-file/jsonLike.js.map +1 -0
  35. package/dist/packlets/json-file/jsonTreeHelper.js.map +1 -0
  36. package/dist/packlets/json-schema-builder/factories.js +342 -0
  37. package/dist/packlets/json-schema-builder/factories.js.map +1 -0
  38. package/dist/packlets/json-schema-builder/fromJson.js +371 -0
  39. package/dist/packlets/json-schema-builder/fromJson.js.map +1 -0
  40. package/dist/packlets/json-schema-builder/index.js +36 -0
  41. package/dist/packlets/json-schema-builder/index.js.map +1 -0
  42. package/dist/{test/unit/json-compatible/helpers.js → packlets/json-schema-builder/types.js} +3 -12
  43. package/dist/packlets/json-schema-builder/types.js.map +1 -0
  44. package/dist/packlets/validators/index.js.map +1 -0
  45. package/dist/packlets/validators/validators.js.map +1 -0
  46. package/dist/ts-json-base.d.ts +473 -36
  47. package/dist/tsdoc-metadata.json +1 -1
  48. package/lib/index.browser.d.ts +2 -1
  49. package/lib/index.browser.d.ts.map +1 -0
  50. package/lib/index.browser.js +3 -1
  51. package/lib/index.browser.js.map +1 -0
  52. package/lib/index.d.ts +2 -1
  53. package/lib/index.d.ts.map +1 -0
  54. package/lib/index.js +3 -1
  55. package/lib/index.js.map +1 -0
  56. package/lib/packlets/converters/converters.d.ts +27 -1
  57. package/lib/packlets/converters/converters.d.ts.map +1 -0
  58. package/lib/packlets/converters/converters.js +12 -0
  59. package/lib/packlets/converters/converters.js.map +1 -0
  60. package/lib/packlets/converters/index.d.ts.map +1 -0
  61. package/lib/packlets/converters/index.js.map +1 -0
  62. package/lib/packlets/file-tree/directoryItem.d.ts.map +1 -0
  63. package/lib/packlets/file-tree/directoryItem.js.map +1 -0
  64. package/lib/packlets/file-tree/fileItem.d.ts +11 -4
  65. package/lib/packlets/file-tree/fileItem.d.ts.map +1 -0
  66. package/lib/packlets/file-tree/fileItem.js +11 -4
  67. package/lib/packlets/file-tree/fileItem.js.map +1 -0
  68. package/lib/packlets/file-tree/fileTree.d.ts.map +1 -0
  69. package/lib/packlets/file-tree/fileTree.js.map +1 -0
  70. package/lib/packlets/file-tree/fileTreeAccessors.d.ts.map +1 -0
  71. package/lib/packlets/file-tree/fileTreeAccessors.js.map +1 -0
  72. package/lib/packlets/file-tree/fileTreeHelpers.d.ts.map +1 -0
  73. package/lib/packlets/file-tree/fileTreeHelpers.inMemory.d.ts.map +1 -0
  74. package/lib/packlets/file-tree/fileTreeHelpers.inMemory.js.map +1 -0
  75. package/lib/packlets/file-tree/fileTreeHelpers.js.map +1 -0
  76. package/lib/packlets/file-tree/filterSpec.d.ts.map +1 -0
  77. package/lib/packlets/file-tree/filterSpec.js.map +1 -0
  78. package/lib/packlets/file-tree/fsTree.d.ts +44 -13
  79. package/lib/packlets/file-tree/fsTree.d.ts.map +1 -0
  80. package/lib/packlets/file-tree/fsTree.js +44 -13
  81. package/lib/packlets/file-tree/fsTree.js.map +1 -0
  82. package/lib/packlets/file-tree/in-memory/inMemoryTree.d.ts +44 -13
  83. package/lib/packlets/file-tree/in-memory/inMemoryTree.d.ts.map +1 -0
  84. package/lib/packlets/file-tree/in-memory/inMemoryTree.js +44 -13
  85. package/lib/packlets/file-tree/in-memory/inMemoryTree.js.map +1 -0
  86. package/lib/packlets/file-tree/in-memory/index.d.ts.map +1 -0
  87. package/lib/packlets/file-tree/in-memory/index.js.map +1 -0
  88. package/lib/packlets/file-tree/in-memory/treeBuilder.d.ts.map +1 -0
  89. package/lib/packlets/file-tree/in-memory/treeBuilder.js.map +1 -0
  90. package/lib/packlets/file-tree/index.browser.d.ts.map +1 -0
  91. package/lib/packlets/file-tree/index.browser.js.map +1 -0
  92. package/lib/packlets/file-tree/index.d.ts.map +1 -0
  93. package/lib/packlets/file-tree/index.js.map +1 -0
  94. package/lib/packlets/json/common.d.ts.map +1 -0
  95. package/lib/packlets/json/common.js.map +1 -0
  96. package/lib/packlets/json/index.d.ts.map +1 -0
  97. package/lib/packlets/json/index.js.map +1 -0
  98. package/lib/packlets/json-compatible/common.d.ts.map +1 -0
  99. package/lib/packlets/json-compatible/common.js.map +1 -0
  100. package/lib/packlets/json-compatible/converters.d.ts.map +1 -0
  101. package/lib/packlets/json-compatible/converters.js.map +1 -0
  102. package/lib/packlets/json-compatible/index.d.ts.map +1 -0
  103. package/lib/packlets/json-compatible/index.js.map +1 -0
  104. package/lib/packlets/json-compatible/validators.d.ts.map +1 -0
  105. package/lib/packlets/json-compatible/validators.js.map +1 -0
  106. package/lib/packlets/json-file/file.d.ts.map +1 -0
  107. package/lib/packlets/json-file/file.js.map +1 -0
  108. package/lib/packlets/json-file/index.browser.d.ts.map +1 -0
  109. package/lib/packlets/json-file/index.browser.js.map +1 -0
  110. package/lib/packlets/json-file/index.d.ts.map +1 -0
  111. package/lib/packlets/json-file/index.js.map +1 -0
  112. package/lib/packlets/json-file/jsonFsHelper.d.ts.map +1 -0
  113. package/lib/packlets/json-file/jsonFsHelper.js.map +1 -0
  114. package/lib/packlets/json-file/jsonLike.d.ts.map +1 -0
  115. package/lib/packlets/json-file/jsonLike.js.map +1 -0
  116. package/lib/packlets/json-file/jsonTreeHelper.d.ts.map +1 -0
  117. package/lib/packlets/json-file/jsonTreeHelper.js.map +1 -0
  118. package/lib/packlets/json-schema-builder/factories.d.ts +95 -0
  119. package/lib/packlets/json-schema-builder/factories.d.ts.map +1 -0
  120. package/lib/packlets/json-schema-builder/factories.js +352 -0
  121. package/lib/packlets/json-schema-builder/factories.js.map +1 -0
  122. package/lib/packlets/json-schema-builder/fromJson.d.ts +45 -0
  123. package/lib/packlets/json-schema-builder/fromJson.d.ts.map +1 -0
  124. package/lib/packlets/json-schema-builder/fromJson.js +375 -0
  125. package/lib/packlets/json-schema-builder/fromJson.js.map +1 -0
  126. package/lib/packlets/json-schema-builder/index.d.ts +15 -0
  127. package/lib/packlets/json-schema-builder/index.d.ts.map +1 -0
  128. package/lib/packlets/json-schema-builder/index.js +52 -0
  129. package/lib/packlets/json-schema-builder/index.js.map +1 -0
  130. package/lib/packlets/json-schema-builder/types.d.ts +142 -0
  131. package/lib/packlets/json-schema-builder/types.d.ts.map +1 -0
  132. package/lib/packlets/json-schema-builder/types.js +24 -0
  133. package/lib/packlets/json-schema-builder/types.js.map +1 -0
  134. package/lib/packlets/validators/index.d.ts.map +1 -0
  135. package/lib/packlets/validators/index.js.map +1 -0
  136. package/lib/packlets/validators/validators.d.ts.map +1 -0
  137. package/lib/packlets/validators/validators.js.map +1 -0
  138. package/package.json +6 -6
  139. package/dist/test/fixtures/file-tree/config.json +0 -1
  140. package/dist/test/fixtures/file-tree/data/items.json +0 -1
  141. package/dist/test/fixtures/file-tree/docs/api/reference.json +0 -1
  142. package/dist/test/unit/data/file/bad/bad3.json +0 -3
  143. package/dist/test/unit/data/file/bad/thing1.json +0 -4
  144. package/dist/test/unit/data/file/bad/thing2.json +0 -3
  145. package/dist/test/unit/data/file/good/thing1.json +0 -4
  146. package/dist/test/unit/data/file/good/thing2.json +0 -3
@@ -0,0 +1,342 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+ import { Converters, Validation, Validators, fail, succeed } from '@fgv/ts-utils';
23
+ // ---------------------------------------------------------------------------
24
+ // Internal helper
25
+ // ---------------------------------------------------------------------------
26
+ // ---------------------------------------------------------------------------
27
+ // Abstract base
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Abstract base class for all schema validator nodes. Extends GenericValidator so
31
+ * each schema value IS a `Validator<T>` and also carries `toJson()` and `_type`.
32
+ * @internal
33
+ */
34
+ class SchemaValidatorBase extends Validation.Base.GenericValidator {
35
+ constructor(type, validator, description) {
36
+ super({ validator });
37
+ this._type = type;
38
+ if (description !== undefined) {
39
+ this.description = description;
40
+ }
41
+ }
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Concrete schema node classes
45
+ // ---------------------------------------------------------------------------
46
+ class StringSchemaValidator extends SchemaValidatorBase {
47
+ constructor(opts) {
48
+ const inner = Validators.string;
49
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
50
+ super('string', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
51
+ this._inner = inner;
52
+ }
53
+ validate(from) {
54
+ return this._inner.validate(from);
55
+ }
56
+ convert(from) {
57
+ return this._inner.validate(from);
58
+ }
59
+ toJson() {
60
+ return Object.assign({ type: 'string' }, _descriptionField(this));
61
+ }
62
+ }
63
+ /**
64
+ * Number/integer schema validator.
65
+ *
66
+ * Strict mode (default): delegates to a Validator that only accepts genuine JSON numbers.
67
+ * Non-strict mode: delegates to a Converter that coerces numeric strings (e.g. '42' -\> 42).
68
+ * Both validate() and convert() route through the same underlying logic so that schema nodes
69
+ * behave correctly when used as field validators inside Converters.object / Converters.arrayOf
70
+ * (which call convert()) and when called directly as Validators (which call validate()).
71
+ */
72
+ class NumberSchemaValidator extends SchemaValidatorBase {
73
+ constructor(type, opts) {
74
+ const strict = (opts === null || opts === void 0 ? void 0 : opts.strict) !== false;
75
+ const isInteger = type === 'integer';
76
+ if (strict) {
77
+ const strictValidator = isInteger
78
+ ? Validators.isA('integer', (v) => typeof v === 'number' && Number.isInteger(v))
79
+ : Validators.isA('number', (v) => typeof v === 'number' && !Number.isNaN(v));
80
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
81
+ super(type, (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
82
+ this._strictValidator = strictValidator;
83
+ this._nonStrictConverter = undefined;
84
+ }
85
+ else {
86
+ // Non-strict: the ValidatorFunc is a placeholder — validate() and convert() are both
87
+ // overridden below to use the coercing Converter, so this lambda is never invoked.
88
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
89
+ super(type, (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
90
+ this._strictValidator = undefined;
91
+ this._nonStrictConverter = isInteger
92
+ ? Converters.number.withConstraint((n) => Number.isInteger(n) || fail(`${n}: not an integer`))
93
+ : Converters.number;
94
+ }
95
+ this.strict = strict;
96
+ }
97
+ validate(from) {
98
+ if (this._nonStrictConverter !== undefined) {
99
+ // Non-strict path: delegate to the coercing Converter so that '42' -> 42.
100
+ return this._nonStrictConverter.convert(from);
101
+ }
102
+ // Strict path: use the strict in-place validator.
103
+ return this._strictValidator.validate(from);
104
+ }
105
+ convert(from) {
106
+ // Symmetric with validate(): both paths route through the same underlying logic.
107
+ // This ensures correctness when used as a field validator inside Converters.object
108
+ // or Converters.arrayOf (which call convert(), not validate()).
109
+ if (this._nonStrictConverter !== undefined) {
110
+ return this._nonStrictConverter.convert(from);
111
+ }
112
+ return this._strictValidator.validate(from);
113
+ }
114
+ toJson() {
115
+ return Object.assign({ type: this._type }, _descriptionField(this));
116
+ }
117
+ }
118
+ class BooleanSchemaValidator extends SchemaValidatorBase {
119
+ constructor(opts) {
120
+ const inner = Validators.boolean;
121
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
122
+ super('boolean', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
123
+ this._inner = inner;
124
+ }
125
+ validate(from) {
126
+ return this._inner.validate(from);
127
+ }
128
+ convert(from) {
129
+ return this._inner.validate(from);
130
+ }
131
+ toJson() {
132
+ return Object.assign({ type: 'boolean' }, _descriptionField(this));
133
+ }
134
+ }
135
+ class EnumSchemaValidator extends SchemaValidatorBase {
136
+ constructor(values, opts) {
137
+ const inner = Validators.enumeratedValue(values);
138
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
139
+ super('enum', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
140
+ this.enum = values;
141
+ this._inner = inner;
142
+ }
143
+ validate(from) {
144
+ return this._inner.validate(from);
145
+ }
146
+ convert(from) {
147
+ return this._inner.validate(from);
148
+ }
149
+ toJson() {
150
+ return Object.assign({ type: 'string', enum: [...this.enum] }, _descriptionField(this));
151
+ }
152
+ }
153
+ class OptionalSchemaValidator extends SchemaValidatorBase {
154
+ constructor(inner) {
155
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
156
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
157
+ super('optional', (__from) => true);
158
+ this._schema = inner;
159
+ }
160
+ validate(from) {
161
+ // Route through convert() so that inner transformations (e.g. non-strict number coercion,
162
+ // object property stripping) are applied even when validate() is called directly.
163
+ return this.convert(from);
164
+ }
165
+ convert(from) {
166
+ if (from === undefined) {
167
+ return succeed(undefined);
168
+ }
169
+ // Delegate to inner.convert() so that coercions and transformations from the inner schema
170
+ // propagate to callers (both Converters.object field evaluation and direct validate() calls).
171
+ return this._schema.convert(from);
172
+ }
173
+ toJson() {
174
+ // optionality is transparent at the JSON Schema level; the parent emits required/optional split.
175
+ return this._schema.toJson();
176
+ }
177
+ }
178
+ class ArraySchemaValidator extends SchemaValidatorBase {
179
+ constructor(items, opts) {
180
+ // Converters.arrayOf calls .convert() on each element, which routes through the item schema's
181
+ // convert() override — correctly propagating coercions (e.g. '42' -> 42 for non-strict numbers).
182
+ const converter = Converters.arrayOf(items);
183
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
184
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
185
+ super('array', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
186
+ this._items = items;
187
+ this._converter = converter;
188
+ }
189
+ validate(from) {
190
+ // Route through convert() so that element coercions from the item schema propagate.
191
+ return this._converter.convert(from);
192
+ }
193
+ convert(from) {
194
+ return this._converter.convert(from);
195
+ }
196
+ toJson() {
197
+ return Object.assign({ type: 'array', items: this._items.toJson() }, _descriptionField(this));
198
+ }
199
+ }
200
+ class ObjectSchemaValidator extends SchemaValidatorBase {
201
+ constructor(properties, opts) {
202
+ const additionalProperties = (opts === null || opts === void 0 ? void 0 : opts.additionalProperties) === true;
203
+ const converter = _buildObjectConverter(properties, additionalProperties);
204
+ // ValidatorFunc placeholder — validate() and convert() are both overridden below.
205
+ /* c8 ignore next 1 - placeholder ValidatorFunc; validate() and convert() overrides always intercept */
206
+ super('object', (__from) => true, opts === null || opts === void 0 ? void 0 : opts.description);
207
+ this._properties = properties;
208
+ this.additionalProperties = additionalProperties;
209
+ this._converter = converter;
210
+ }
211
+ validate(from) {
212
+ return this._converter.convert(from);
213
+ }
214
+ convert(from) {
215
+ return this._converter.convert(from);
216
+ }
217
+ toJson() {
218
+ const properties = {};
219
+ const required = [];
220
+ for (const [key, prop] of Object.entries(this._properties)) {
221
+ // OptionalSchemaValidator.toJson() delegates to the inner schema, so optionality is
222
+ // transparent at the wire level and managed via the `required` array.
223
+ properties[key] = prop.toJson();
224
+ if (prop._type !== 'optional') {
225
+ required.push(key);
226
+ }
227
+ }
228
+ return Object.assign(Object.assign(Object.assign({ type: 'object', properties }, (required.length > 0 && { required })), (!this.additionalProperties && { additionalProperties: false })), _descriptionField(this));
229
+ }
230
+ }
231
+ // ---------------------------------------------------------------------------
232
+ // Private builder helpers
233
+ // ---------------------------------------------------------------------------
234
+ /**
235
+ * Builds a `Converter<ObjectStatic<P>>` for object nodes using `Converters.object`.
236
+ * Uses a Converter (not Validator) so that extra properties are stripped from the result —
237
+ * Validators are in-place and would pass unrecognized fields through unchanged.
238
+ * Optional properties are tracked and listed in `optionalFields`.
239
+ * Each property's ISchemaValidator is used as the field converter — its convert() method
240
+ * is called by ObjectConverter for each field, propagating nested coercions and transforms.
241
+ */
242
+ function _buildObjectConverter(properties, additionalProperties) {
243
+ const fields = {};
244
+ const optionalKeys = [];
245
+ for (const [key, prop] of Object.entries(properties)) {
246
+ // Each prop IS both a Validator<unknown> and a Converter<unknown> (ISchemaValidator extends both,
247
+ // and all schema classes override convert()). We cast to Validator<unknown> for the field-map
248
+ // construction; ObjectConverter calls .convert() on each field, which routes through the schema's
249
+ // convert() override and propagates nested coercions correctly.
250
+ fields[key] = prop;
251
+ if (prop._type === 'optional') {
252
+ optionalKeys.push(key);
253
+ }
254
+ }
255
+ return Converters.object(fields, {
256
+ optionalFields: optionalKeys,
257
+ strict: !additionalProperties
258
+ });
259
+ }
260
+ function _descriptionField(schema) {
261
+ return schema.description !== undefined ? { description: schema.description } : {};
262
+ }
263
+ // ---------------------------------------------------------------------------
264
+ // Public factory functions
265
+ // ---------------------------------------------------------------------------
266
+ /**
267
+ * Creates a schema node for a JSON `string`.
268
+ * @param opts - Optional description.
269
+ * @returns An `ISchemaValidator` whose `Static` type is `string`.
270
+ * @public
271
+ */
272
+ export function string(opts) {
273
+ return new StringSchemaValidator(opts);
274
+ }
275
+ /**
276
+ * Creates a schema node for a JSON `number`.
277
+ * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).
278
+ * @returns An `ISchemaValidator` whose `Static` type is `number`.
279
+ * @public
280
+ */
281
+ export function number(opts) {
282
+ return new NumberSchemaValidator('number', opts);
283
+ }
284
+ /**
285
+ * Creates a schema node for a JSON `integer`.
286
+ * @param opts - Optional description and strict mode (see `INumberSchemaOptions`).
287
+ * @returns An `ISchemaValidator` (tagged `integer`) whose `Static` type is `number`.
288
+ * @public
289
+ */
290
+ export function integer(opts) {
291
+ return new NumberSchemaValidator('integer', opts);
292
+ }
293
+ /**
294
+ * Creates a schema node for a JSON `boolean`.
295
+ * @param opts - Optional description.
296
+ * @returns An `ISchemaValidator` whose `Static` type is `boolean`.
297
+ * @public
298
+ */
299
+ export function boolean(opts) {
300
+ return new BooleanSchemaValidator(opts);
301
+ }
302
+ /**
303
+ * Creates a schema node for a closed set of string literals.
304
+ * @param values - The allowed string values.
305
+ * @param opts - Optional description.
306
+ * @returns An `ISchemaValidator` whose `Static` type is the union of `values`.
307
+ * @public
308
+ */
309
+ export function enumOf(values, opts) {
310
+ return new EnumSchemaValidator(values, opts);
311
+ }
312
+ /**
313
+ * Marks a property schema as optional within an `object` schema.
314
+ * @param schema - The inner schema to make optional.
315
+ * @returns An `ISchemaValidator` whose `Static` type is `Static<S> | undefined`.
316
+ * @public
317
+ */
318
+ export function optional(schema) {
319
+ return new OptionalSchemaValidator(schema);
320
+ }
321
+ /**
322
+ * Creates a schema node for a JSON `array` whose elements all match `items`.
323
+ * @param items - The schema applied to every element.
324
+ * @param opts - Optional description.
325
+ * @returns An `ISchemaValidator` whose `Static` type is `Array<Static<S>>`.
326
+ * @public
327
+ */
328
+ export function array(items, opts) {
329
+ return new ArraySchemaValidator(items, opts);
330
+ }
331
+ /**
332
+ * Creates a schema node for a JSON `object` with a fixed set of typed properties.
333
+ * @param properties - A record mapping property names to their schemas. Wrap a property with
334
+ * `optional` to make it optional.
335
+ * @param opts - Optional description and `additionalProperties` flag.
336
+ * @returns An `ISchemaValidator` whose `Static` type derives the required/optional split.
337
+ * @public
338
+ */
339
+ export function object(properties, opts) {
340
+ return new ObjectSchemaValidator(properties, opts);
341
+ }
342
+ //# sourceMappingURL=factories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.js","sourceRoot":"","sources":["../../../src/packlets/json-schema-builder/factories.ts"],"names":[],"mappings":"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","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"]}