@schema-ts/core 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -88,7 +88,42 @@ declare function detectSchemaType(value: unknown): string;
88
88
  declare function parseJsonPointer(jsonPointer: string): string[];
89
89
  declare function getJsonPointer(obj: unknown, jsonPointer: string): unknown;
90
90
  declare function removeJsonPointer(obj: unknown, jsonPointer: string): boolean;
91
+ /**
92
+ * Sets a value at the specified JSON Pointer path within an object.
93
+ *
94
+ * This function modifies the object in place by setting a value at the location
95
+ * specified by the JSON Pointer. If intermediate paths don't exist, they will be
96
+ * automatically created as objects or arrays based on the next path segment
97
+ * (numeric segments create arrays, non-numeric create objects).
98
+ *
99
+ * @param obj - The object to modify. Must be a non-null object or array.
100
+ * @param jsonPointer - A JSON Pointer string (RFC 6901) indicating where to set the value.
101
+ * Examples: "/foo", "/foo/0", "/foo/bar/baz"
102
+ * @param value - The value to set at the specified location.
103
+ * @returns `true` if the value was successfully set, `false` otherwise.
104
+ *
105
+ * @remarks
106
+ * - Returns `false` if `jsonPointer` is an empty string (cannot replace root object).
107
+ * To replace the entire object, handle this case in the calling code.
108
+ * - Returns `false` if `obj` is null or undefined.
109
+ * - For array paths, automatically extends the array if the index is beyond current length.
110
+ * - Automatically initializes missing intermediate containers as arrays or objects.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const obj = { foo: { bar: 1 } };
115
+ * setJsonPointer(obj, "/foo/bar", 2); // true, obj.foo.bar is now 2
116
+ * setJsonPointer(obj, "/foo/baz", 3); // true, obj.foo.baz is now 3
117
+ * setJsonPointer(obj, "/new/path", 4); // true, obj.new.path is now 4
118
+ * setJsonPointer(obj, "", 5); // false, cannot replace root object
119
+ *
120
+ * const arr = [1, 2, 3];
121
+ * setJsonPointer(arr, "/0", 10); // true, arr[0] is now 10
122
+ * setJsonPointer(arr, "/5", 20); // true, arr is now [10, 2, 3, undefined, undefined, 20]
123
+ * ```
124
+ */
91
125
  declare function setJsonPointer(obj: unknown, jsonPointer: string, value: unknown): boolean;
126
+ declare function getJsonPointerParent(path: string): string;
92
127
  declare function get(obj: unknown, path: string[]): unknown;
93
128
  /**
94
129
  * Deep equality comparison for two values.
@@ -175,6 +210,103 @@ declare class Validator {
175
210
  */
176
211
  declare function validateSchema(schema: Schema | unknown, value: unknown, instancePath?: string, schemaPath?: string, fastFail?: boolean): Output;
177
212
 
213
+ /**
214
+ * JSON Schema draft version definitions and detection utilities.
215
+ */
216
+ /**
217
+ * Supported JSON Schema draft versions.
218
+ */
219
+ type SchemaDraft = "draft-04" | "draft-07" | "draft-2019-09" | "draft-2020-12";
220
+ /**
221
+ * Standard $schema URI patterns for each draft version.
222
+ */
223
+ declare const DRAFT_URIS: Record<SchemaDraft, string[]>;
224
+ /**
225
+ * Detect the JSON Schema draft version from a schema.
226
+ * Detection is based on:
227
+ * 1. The $schema URI if present
228
+ * 2. Heuristics based on keywords used
229
+ *
230
+ * @param schema - The schema to detect the version of (can be object, boolean, or unknown)
231
+ * @returns The detected draft version, defaults to "draft-2020-12" if unknown
232
+ */
233
+ declare function detectSchemaDraft(schema: unknown): SchemaDraft;
234
+
235
+ /**
236
+ * Schema normalizer that transforms older JSON Schema drafts to draft-2020-12.
237
+ *
238
+ * This allows the validator to only implement draft-2020-12 semantics
239
+ * while still supporting schemas written for older drafts.
240
+ */
241
+
242
+ /**
243
+ * Schema normalizer interface for transforming various JSON Schema formats into a unified version.
244
+ */
245
+ interface Normalizer {
246
+ /**
247
+ * Normalizes the given schema.
248
+ * @param schema The raw schema input
249
+ * @returns A schema object following the latest draft specification
250
+ */
251
+ normalize(schema: unknown): Schema;
252
+ }
253
+ /**
254
+ * Base draft normalizer responsible for converting older JSON Schema keywords to newer versions (e.g., Draft-04 to Draft-2020-12).
255
+ */
256
+ declare class DraftNormalizer implements Normalizer {
257
+ normalize(schema: unknown): Schema;
258
+ }
259
+ interface DraftNormalizerOptions {
260
+ /**
261
+ * Source draft version. If not specified, will be auto-detected.
262
+ */
263
+ sourceDraft?: SchemaDraft;
264
+ }
265
+ /**
266
+ * Normalize a JSON Schema from any supported draft to draft-2020-12 format.
267
+ *
268
+ * Transformations performed:
269
+ * - `id` → `$id` (draft-04)
270
+ * - Boolean `exclusiveMaximum`/`exclusiveMinimum` → numeric values (draft-04)
271
+ * - Array `items` + `additionalItems` → `prefixItems` + `items` (draft-04/07/2019-09)
272
+ * - `dependencies` → `dependentRequired`/`dependentSchemas` (draft-04/07)
273
+ * - `$recursiveRef`/`$recursiveAnchor` → `$dynamicRef`/`$dynamicAnchor` (draft-2019-09)
274
+ *
275
+ * @param schema - The schema to normalize (can be object, boolean, or unknown)
276
+ * @param options - Optional configuration
277
+ * @returns A new schema object in draft-2020-12 format (always returns an object)
278
+ */
279
+ declare function normalizeSchema(schema: unknown, options?: DraftNormalizerOptions): Schema;
280
+ /**
281
+ * Enhanced normalizer that adds type inference, automatic required field identification,
282
+ * and support for OpenAPI/Kubernetes extension properties on top of base draft transformations.
283
+ */
284
+ declare class BetterNormalizer implements Normalizer {
285
+ /**
286
+ * Performs the normalization process:
287
+ * 1. Base draft normalization (DraftNormalizer)
288
+ * 2. Enhanced default value completion (Better logic)
289
+ */
290
+ normalize(schema: unknown): Schema;
291
+ /**
292
+ * Recursively processes all sub-nodes of the schema, applying default rules.
293
+ */
294
+ private default;
295
+ /**
296
+ * Batch processes Record-type sub-schemas.
297
+ */
298
+ private defaultSchemaMap;
299
+ /**
300
+ * Applies inference and heuristic rules to a single schema object.
301
+ * Includes:
302
+ * - `type` inference based on properties (object/array)
303
+ * - Automatically setting properties with `const`/`default`/`enum` as `required`
304
+ * - Handling `required` fields from OpenAPI `discriminator`
305
+ * - Handling `x-required` and `x-kubernetes-patch-merge-key`
306
+ */
307
+ private defaultSchema;
308
+ }
309
+
178
310
  /**
179
311
  * Represents a node in the schema tree.
180
312
  * Each node corresponds to a location in both the schema and the instance data.
@@ -187,11 +319,13 @@ interface FieldNode {
187
319
  children?: FieldNode[];
188
320
  instanceLocation: string;
189
321
  keywordLocation: string;
322
+ originKeywordLocation?: string;
190
323
  version: number;
191
324
  dependencies?: Set<string>;
192
325
  canRemove: boolean;
193
326
  canAdd: boolean;
194
- isRequired: boolean;
327
+ required: boolean;
328
+ activated: boolean;
195
329
  }
196
330
  type SchemaChangeEvent = {
197
331
  type: "schema" | "value" | "error";
@@ -201,22 +335,26 @@ interface SchemaRuntimeOptions {
201
335
  /**
202
336
  * Control how default values are applied when schema changes.
203
337
  * - 'always': Fill all type-based defaults (e.g., [] for array, "" for string)
204
- * - 'explicit': Only fill explicitly declared defaults (schema.default)
205
- * - 'never': Never auto-fill defaults
206
- * @default 'explicit'
338
+ * - 'auto': Fill defaults by defaulting rules (required fields, const, default keyword etc.)
339
+ * @default 'auto'
207
340
  */
208
- autoFillDefaults?: "always" | "explicit" | "never";
341
+ fillDefaults?: "auto" | "never";
209
342
  /**
210
343
  * Control behavior when removing the last element from an array or object.
211
344
  * - 'never': Always keep empty containers ([] or {})
212
- * - 'auto': Remove empty container only if the field is optional (not in parent's required array)
213
- * - 'always': Always remove empty containers (may cause validation errors for required fields)
345
+ * - 'auto': Remove empty container only following rules.
214
346
  * @default 'auto'
215
347
  */
216
- removeEmptyContainers?: "never" | "auto" | "always";
348
+ removeEmptyContainers?: "never" | "auto";
349
+ /**
350
+ * Custom schema normalizer.
351
+ * If specified, will use this normalizer instead of the default one.
352
+ */
353
+ schemaNormalizer?: Normalizer;
217
354
  }
218
355
  declare class SchemaRuntime {
219
356
  private validator;
357
+ private normalizer;
220
358
  private watchers;
221
359
  private globalWatchers;
222
360
  private dependentsMap;
@@ -240,6 +378,12 @@ declare class SchemaRuntime {
240
378
  * const runtime = new SchemaRuntime(validator, schema, { name: "Alice" });
241
379
  */
242
380
  constructor(validator: Validator, schema: Schema | unknown, value: unknown, options?: SchemaRuntimeOptions);
381
+ private resolveSchema;
382
+ /**
383
+ * Update the entire schema.
384
+ * This triggers a full rebuild of the node tree while preserving the current value.
385
+ */
386
+ setSchema(schema: Schema | unknown): void;
243
387
  /**
244
388
  * Register a node as dependent on a path
245
389
  */
@@ -302,11 +446,6 @@ declare class SchemaRuntime {
302
446
  * @param event - The change event containing type and path
303
447
  */
304
448
  notify(event: SchemaChangeEvent): void;
305
- /**
306
- * Update the entire schema.
307
- * This triggers a full rebuild of the node tree while preserving the current value.
308
- */
309
- setSchema(schema: Schema | unknown): void;
310
449
  /**
311
450
  * Get the value at a specific path.
312
451
  *
@@ -322,11 +461,11 @@ declare class SchemaRuntime {
322
461
  * Internal helper to set a value at a normalized path.
323
462
  * Handles both root and non-root paths.
324
463
  *
325
- * @param normalizedPath - The normalized JSON Pointer path
464
+ * @param path - The JSON Pointer path
326
465
  * @param value - The value to set
327
466
  * @returns true if successful, false if the path cannot be set
328
467
  */
329
- private setValueAtPath;
468
+ private setJsonPointer;
330
469
  /**
331
470
  * Internal method to remove a value at a path.
332
471
  * Shared logic for removeValue and setValue(undefined).
@@ -347,14 +486,17 @@ declare class SchemaRuntime {
347
486
  * Clean up empty parent containers after element removal.
348
487
  * Recursively removes empty arrays/objects based on removeEmptyContainers option.
349
488
  * @param path - The path to check for empty container
350
- * @returns The topmost parent path for reconciliation
489
+ * @returns The topmost parent path changed
351
490
  */
352
491
  private cleanupEmptyContainers;
353
492
  /**
354
493
  * Get default value for a schema, respecting autoFillDefaults option.
355
494
  * Falls back to 'always' strategy if configured strategy returns undefined.
495
+ * If still undefined (e.g., schema has no type), falls back to null as a valid JSON value.
356
496
  */
357
- private getDefaultValueForAdd;
497
+ private newDefaultValue;
498
+ private addObjectProperty;
499
+ private addArrayItem;
358
500
  /**
359
501
  * Add a new child to an array or object at the specified parent path.
360
502
  * For arrays, appends a new item with default value based on items schema.
@@ -364,7 +506,7 @@ declare class SchemaRuntime {
364
506
  * @param initialValue - Optional initial value to set. If not provided, uses default from schema.
365
507
  * @returns true if successful, false if cannot add
366
508
  */
367
- addValue(parentPath: string, key?: string, initialValue?: unknown): boolean;
509
+ addChild(parentPath: string, key?: string, initialValue?: unknown): boolean;
368
510
  /**
369
511
  * Set the value at a specific path.
370
512
  * Creates intermediate containers (objects/arrays) as needed.
@@ -384,18 +526,19 @@ declare class SchemaRuntime {
384
526
  */
385
527
  setValue(path: string, value: unknown): boolean;
386
528
  /**
387
- * Find the FieldNode at a specific path.
529
+ * Get the FieldNode at a specific path.
388
530
  * Returns the node tree representation that includes schema, type, error, and children.
389
531
  *
390
532
  * @param path - The JSON Pointer path (e.g., "/user/name", "" for root)
391
533
  * @returns The FieldNode at the path, or undefined if not found
392
534
  *
393
535
  * @example
394
- * const node = runtime.findNode("/name");
536
+ * const node = runtime.getNode("/name");
395
537
  * console.log(node?.schema, node?.type, node?.error);
396
538
  */
397
- findNode(path: string): FieldNode | undefined;
539
+ getNode(path: string): FieldNode | undefined;
398
540
  private findNearestExistingNode;
541
+ validate(path: string): void;
399
542
  /**
400
543
  * Update node dependencies when schema changes.
401
544
  * Unregisters old dependencies and registers new ones.
@@ -407,15 +550,21 @@ declare class SchemaRuntime {
407
550
  * may appear when conditions change.
408
551
  *
409
552
  * Container initialization rules:
410
- * - Root node is always considered required and will be initialized if it has defaults or required properties
553
+ * - Required containers will be initialized if it has defaults or required properties
411
554
  * - Nested containers are initialized only if they are in parent's required array
412
555
  *
413
556
  * @param instanceLocation - The path to the node
414
557
  * @param newSchema - The new effective schema
415
558
  * @param type - The schema type
416
- * @param isRequired - Whether this node is required by its parent
559
+ * @param required - Whether this node is required by its parent
417
560
  */
418
561
  private applySchemaDefaults;
562
+ /**
563
+ * Sort property entries by x-order.
564
+ * Properties with lower x-order values come first.
565
+ * Properties without x-order are placed at the end.
566
+ */
567
+ private sortPropertiesByOrder;
419
568
  /**
420
569
  * Build children for object and array nodes.
421
570
  * Reuses existing child nodes where possible.
@@ -430,57 +579,6 @@ declare class SchemaRuntime {
430
579
  private buildNode;
431
580
  }
432
581
 
433
- /**
434
- * JSON Schema draft version definitions and detection utilities.
435
- */
436
- /**
437
- * Supported JSON Schema draft versions.
438
- */
439
- type SchemaDraft = "draft-04" | "draft-07" | "draft-2019-09" | "draft-2020-12";
440
- /**
441
- * Standard $schema URI patterns for each draft version.
442
- */
443
- declare const DRAFT_URIS: Record<SchemaDraft, string[]>;
444
- /**
445
- * Detect the JSON Schema draft version from a schema.
446
- * Detection is based on:
447
- * 1. The $schema URI if present
448
- * 2. Heuristics based on keywords used
449
- *
450
- * @param schema - The schema to detect the version of (can be object, boolean, or unknown)
451
- * @returns The detected draft version, defaults to "draft-2020-12" if unknown
452
- */
453
- declare function detectSchemaDraft(schema: unknown): SchemaDraft;
454
-
455
- /**
456
- * Schema normalizer that transforms older JSON Schema drafts to draft-2020-12.
457
- *
458
- * This allows the validator to only implement draft-2020-12 semantics
459
- * while still supporting schemas written for older drafts.
460
- */
461
-
462
- interface NormalizerOptions {
463
- /**
464
- * Source draft version. If not specified, will be auto-detected.
465
- */
466
- sourceDraft?: SchemaDraft;
467
- }
468
- /**
469
- * Normalize a JSON Schema from any supported draft to draft-2020-12 format.
470
- *
471
- * Transformations performed:
472
- * - `id` → `$id` (draft-04)
473
- * - Boolean `exclusiveMaximum`/`exclusiveMinimum` → numeric values (draft-04)
474
- * - Array `items` + `additionalItems` → `prefixItems` + `items` (draft-04/07/2019-09)
475
- * - `dependencies` → `dependentRequired`/`dependentSchemas` (draft-04/07)
476
- * - `$recursiveRef`/`$recursiveAnchor` → `$dynamicRef`/`$dynamicAnchor` (draft-2019-09)
477
- *
478
- * @param schema - The schema to normalize (can be object, boolean, or unknown)
479
- * @param options - Optional configuration
480
- * @returns A new schema object in draft-2020-12 format (always returns an object)
481
- */
482
- declare function normalizeSchema(schema: unknown, options?: NormalizerOptions): Schema;
483
-
484
582
  /**
485
583
  * Collect all dependencies for a node's schema.
486
584
  *
@@ -510,6 +608,7 @@ interface GetDefaultValueOptions {
510
608
  */
511
609
  strategy?: "always" | "explicit";
512
610
  }
611
+ declare function typeNullable(schema: Schema): [string | undefined, boolean];
513
612
  /**
514
613
  * Generate a default value for a schema based on its type and constraints.
515
614
  *
@@ -524,21 +623,10 @@ interface GetDefaultValueOptions {
524
623
  * If a value is provided, it will be used as the base and missing defaults will be filled in.
525
624
  *
526
625
  * @param schema - The JSON Schema to generate a default value for
527
- * @param options - Optional configuration for default value generation
626
+ * @param strategy - Controls type-based default behavior
528
627
  * @returns The generated default value, or undefined if type cannot be determined
529
- *
530
- * @example
531
- * getDefaultValue({ type: "string" }) // returns undefined (explicit mode)
532
- * getDefaultValue({ type: "string" }, { strategy: 'always' }) // returns ""
533
- * getDefaultValue({ type: "number", default: 42 }) // returns 42
534
- * getDefaultValue({ const: "fixed" }) // returns "fixed"
535
- * getDefaultValue({
536
- * type: "object",
537
- * properties: { name: { type: "string" } },
538
- * required: ["name"]
539
- * }, { strategy: 'always' }) // returns { name: "" }
540
- * getDefaultValue({ type: "object", properties: { a: { default: 1 } } }, { value: {} }) // returns { a: 1 }
541
628
  */
542
- declare function getDefaultValue(schema: Schema, options?: GetDefaultValueOptions): unknown;
629
+ declare function getDefaultValue(schema: Schema, required?: boolean): unknown;
630
+ declare function applyDefaults(type: string, value: unknown, schema: Schema, required?: boolean): [unknown, boolean];
543
631
 
544
- export { DRAFT_URIS, type ErrorFormatter, type ErrorMessage, type FieldNode, type GetDefaultValueOptions, MESSAGES, type NormalizerOptions, type Output, type Schema, type SchemaChangeEvent, type SchemaDraft, SchemaRuntime, type SchemaRuntimeOptions, type SchemaType, StringFormatValidator, type StringFormatValidatorInterface, Validator, type ValidatorConfig, type ValidatorOptions, collectDependencies, deepEqual, defaultErrorFormatter, detectSchemaDraft, detectSchemaType, extractReferencedPaths, get, getDefaultValue, getJsonPointer, jsonPointerEscape, jsonPointerJoin, jsonPointerUnescape, matchSchemaType, normalizeSchema, parseJsonPointer, removeJsonPointer, resolveAbsolutePath, safeRegexTest, setJsonPointer, stringFormatValidator, validateSchema };
632
+ export { BetterNormalizer, DRAFT_URIS, DraftNormalizer, type DraftNormalizerOptions, type ErrorFormatter, type ErrorMessage, type FieldNode, type GetDefaultValueOptions, MESSAGES, type Normalizer, type Output, type Schema, type SchemaChangeEvent, type SchemaDraft, SchemaRuntime, type SchemaRuntimeOptions, type SchemaType, StringFormatValidator, type StringFormatValidatorInterface, Validator, type ValidatorConfig, type ValidatorOptions, applyDefaults, collectDependencies, deepEqual, defaultErrorFormatter, detectSchemaDraft, detectSchemaType, extractReferencedPaths, get, getDefaultValue, getJsonPointer, getJsonPointerParent, jsonPointerEscape, jsonPointerJoin, jsonPointerUnescape, matchSchemaType, normalizeSchema, parseJsonPointer, removeJsonPointer, resolveAbsolutePath, safeRegexTest, setJsonPointer, stringFormatValidator, typeNullable, validateSchema };