@schema-ts/core 0.1.2 → 0.1.4

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.cts CHANGED
@@ -191,11 +191,30 @@ interface FieldNode {
191
191
  dependencies?: Set<string>;
192
192
  canRemove: boolean;
193
193
  canAdd: boolean;
194
+ isRequired: boolean;
194
195
  }
195
196
  type SchemaChangeEvent = {
196
197
  type: "schema" | "value" | "error";
197
198
  path: string;
198
199
  };
200
+ interface SchemaRuntimeOptions {
201
+ /**
202
+ * Control how default values are applied when schema changes.
203
+ * - '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'
207
+ */
208
+ autoFillDefaults?: "always" | "explicit" | "never";
209
+ /**
210
+ * Control behavior when removing the last element from an array or object.
211
+ * - '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)
214
+ * @default 'auto'
215
+ */
216
+ removeEmptyContainers?: "never" | "auto" | "always";
217
+ }
199
218
  declare class SchemaRuntime {
200
219
  private validator;
201
220
  private watchers;
@@ -206,19 +225,21 @@ declare class SchemaRuntime {
206
225
  private value;
207
226
  private version;
208
227
  private rootSchema;
228
+ private options;
209
229
  /**
210
230
  * Create a new SchemaRuntime instance.
211
231
  *
212
232
  * @param validator - The validator instance for schema validation
213
233
  * @param schema - The JSON Schema definition (will be normalized and dereferenced)
214
234
  * @param value - The initial data value to manage
235
+ * @param options - Runtime configuration options
215
236
  *
216
237
  * @example
217
238
  * const validator = new Validator();
218
239
  * const schema = { type: "object", properties: { name: { type: "string" } } };
219
240
  * const runtime = new SchemaRuntime(validator, schema, { name: "Alice" });
220
241
  */
221
- constructor(validator: Validator, schema: Schema | unknown, value: unknown);
242
+ constructor(validator: Validator, schema: Schema | unknown, value: unknown, options?: SchemaRuntimeOptions);
222
243
  /**
223
244
  * Register a node as dependent on a path
224
245
  */
@@ -297,13 +318,43 @@ declare class SchemaRuntime {
297
318
  * runtime.getValue("/name"); // returns value at /name
298
319
  */
299
320
  getValue(path: string): unknown;
321
+ /**
322
+ * Internal helper to set a value at a normalized path.
323
+ * Handles both root and non-root paths.
324
+ *
325
+ * @param normalizedPath - The normalized JSON Pointer path
326
+ * @param value - The value to set
327
+ * @returns true if successful, false if the path cannot be set
328
+ */
329
+ private setValueAtPath;
330
+ /**
331
+ * Internal method to remove a value at a path.
332
+ * Shared logic for removeValue and setValue(undefined).
333
+ * @param path - The normalized path to remove
334
+ * @param canRemove - Whether the removal is allowed (pre-checked by caller)
335
+ * @returns true if successful, false if removal failed
336
+ */
337
+ private removeValueInternal;
300
338
  /**
301
339
  * Remove a node at the specified path.
302
340
  * This deletes the value from the data structure (array splice or object delete).
341
+ * After removal, may also remove empty parent containers based on removeEmptyContainers option.
303
342
  * @param path - The path to remove
304
343
  * @returns true if successful, false if the path cannot be removed
305
344
  */
306
345
  removeValue(path: string): boolean;
346
+ /**
347
+ * Clean up empty parent containers after element removal.
348
+ * Recursively removes empty arrays/objects based on removeEmptyContainers option.
349
+ * @param path - The path to check for empty container
350
+ * @returns The topmost parent path for reconciliation
351
+ */
352
+ private cleanupEmptyContainers;
353
+ /**
354
+ * Get default value for a schema, respecting autoFillDefaults option.
355
+ * Falls back to 'always' strategy if configured strategy returns undefined.
356
+ */
357
+ private getDefaultValueForAdd;
307
358
  /**
308
359
  * Add a new child to an array or object at the specified parent path.
309
360
  * For arrays, appends a new item with default value based on items schema.
@@ -319,13 +370,17 @@ declare class SchemaRuntime {
319
370
  * Creates intermediate containers (objects/arrays) as needed.
320
371
  * Triggers reconciliation and notifies subscribers.
321
372
  *
373
+ * When value is undefined and the field is not required, the field will be
374
+ * removed from the parent container (similar to removeValue behavior).
375
+ *
322
376
  * @param path - The JSON Pointer path (e.g., "/user/name", "" for root)
323
- * @param value - The new value to set
377
+ * @param value - The new value to set. If undefined and field is optional, removes the field.
324
378
  * @returns true if successful, false if the path cannot be set
325
379
  *
326
380
  * @example
327
381
  * runtime.setValue("/name", "Bob"); // set name to "Bob"
328
382
  * runtime.setValue("", { name: "Alice" }); // replace entire root value
383
+ * runtime.setValue("/optional", undefined); // remove optional field
329
384
  */
330
385
  setValue(path: string, value: unknown): boolean;
331
386
  /**
@@ -351,9 +406,14 @@ declare class SchemaRuntime {
351
406
  * This handles cases like if-then-else where new properties with defaults
352
407
  * may appear when conditions change.
353
408
  *
409
+ * Container initialization rules:
410
+ * - Root node is always considered required and will be initialized if it has defaults or required properties
411
+ * - Nested containers are initialized only if they are in parent's required array
412
+ *
354
413
  * @param instanceLocation - The path to the node
355
414
  * @param newSchema - The new effective schema
356
415
  * @param type - The schema type
416
+ * @param isRequired - Whether this node is required by its parent
357
417
  */
358
418
  private applySchemaDefaults;
359
419
  /**
@@ -365,6 +425,7 @@ declare class SchemaRuntime {
365
425
  * Build/update a FieldNode in place.
366
426
  * Updates the node's schema, type, error, and children based on the current value.
367
427
  * @param schema - Optional. If provided, updates node.originalSchema. Otherwise uses existing.
428
+ * @param isRequired - Whether this node is required by its parent schema.
368
429
  */
369
430
  private buildNode;
370
431
  }
@@ -439,19 +500,23 @@ declare function collectDependencies(schema: Schema, instanceLocation: string):
439
500
  */
440
501
  declare function extractReferencedPaths(conditionSchema: Schema, basePath?: string, depth?: number): string[];
441
502
 
503
+ interface GetDefaultValueOptions {
504
+ /** Optional existing value to fill defaults into */
505
+ value?: unknown;
506
+ /**
507
+ * Controls type-based default behavior:
508
+ * - 'always': Returns type-based defaults (e.g., "" for string, [] for array)
509
+ * - 'explicit' (default): Only returns explicitly declared schema.const or schema.default
510
+ */
511
+ strategy?: "always" | "explicit";
512
+ }
442
513
  /**
443
514
  * Generate a default value for a schema based on its type and constraints.
444
515
  *
445
516
  * Priority order for determining the default value:
446
517
  * 1. const - if defined, returns the const value
447
518
  * 2. default - if defined, returns the default value
448
- * 3. Type-based defaults:
449
- * - string: ""
450
- * - number/integer: 0
451
- * - boolean: false
452
- * - null: null
453
- * - object: {} with required properties recursively initialized
454
- * - array: []
519
+ * 3. Type-based defaults (controlled by strategy):
455
520
  *
456
521
  * For objects, only required properties are initialized with their default values.
457
522
  * If no type is specified but properties exist, the schema is treated as an object.
@@ -459,20 +524,21 @@ declare function extractReferencedPaths(conditionSchema: Schema, basePath?: stri
459
524
  * If a value is provided, it will be used as the base and missing defaults will be filled in.
460
525
  *
461
526
  * @param schema - The JSON Schema to generate a default value for
462
- * @param value - Optional existing value to fill defaults into
527
+ * @param options - Optional configuration for default value generation
463
528
  * @returns The generated default value, or undefined if type cannot be determined
464
529
  *
465
530
  * @example
466
- * getDefaultValue({ type: "string" }) // returns ""
531
+ * getDefaultValue({ type: "string" }) // returns undefined (explicit mode)
532
+ * getDefaultValue({ type: "string" }, { strategy: 'always' }) // returns ""
467
533
  * getDefaultValue({ type: "number", default: 42 }) // returns 42
468
534
  * getDefaultValue({ const: "fixed" }) // returns "fixed"
469
535
  * getDefaultValue({
470
536
  * type: "object",
471
537
  * properties: { name: { type: "string" } },
472
538
  * required: ["name"]
473
- * }) // returns { name: "" }
474
- * getDefaultValue({ type: "object", properties: { a: { default: 1 } } }, {}) // returns { a: 1 }
539
+ * }, { strategy: 'always' }) // returns { name: "" }
540
+ * getDefaultValue({ type: "object", properties: { a: { default: 1 } } }, { value: {} }) // returns { a: 1 }
475
541
  */
476
- declare function getDefaultValue(schema: Schema, value?: unknown): unknown;
542
+ declare function getDefaultValue(schema: Schema, options?: GetDefaultValueOptions): unknown;
477
543
 
478
- export { DRAFT_URIS, type ErrorFormatter, type ErrorMessage, type FieldNode, MESSAGES, type NormalizerOptions, type Output, type Schema, type SchemaChangeEvent, type SchemaDraft, SchemaRuntime, 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 };
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 };
package/dist/index.d.ts CHANGED
@@ -191,11 +191,30 @@ interface FieldNode {
191
191
  dependencies?: Set<string>;
192
192
  canRemove: boolean;
193
193
  canAdd: boolean;
194
+ isRequired: boolean;
194
195
  }
195
196
  type SchemaChangeEvent = {
196
197
  type: "schema" | "value" | "error";
197
198
  path: string;
198
199
  };
200
+ interface SchemaRuntimeOptions {
201
+ /**
202
+ * Control how default values are applied when schema changes.
203
+ * - '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'
207
+ */
208
+ autoFillDefaults?: "always" | "explicit" | "never";
209
+ /**
210
+ * Control behavior when removing the last element from an array or object.
211
+ * - '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)
214
+ * @default 'auto'
215
+ */
216
+ removeEmptyContainers?: "never" | "auto" | "always";
217
+ }
199
218
  declare class SchemaRuntime {
200
219
  private validator;
201
220
  private watchers;
@@ -206,19 +225,21 @@ declare class SchemaRuntime {
206
225
  private value;
207
226
  private version;
208
227
  private rootSchema;
228
+ private options;
209
229
  /**
210
230
  * Create a new SchemaRuntime instance.
211
231
  *
212
232
  * @param validator - The validator instance for schema validation
213
233
  * @param schema - The JSON Schema definition (will be normalized and dereferenced)
214
234
  * @param value - The initial data value to manage
235
+ * @param options - Runtime configuration options
215
236
  *
216
237
  * @example
217
238
  * const validator = new Validator();
218
239
  * const schema = { type: "object", properties: { name: { type: "string" } } };
219
240
  * const runtime = new SchemaRuntime(validator, schema, { name: "Alice" });
220
241
  */
221
- constructor(validator: Validator, schema: Schema | unknown, value: unknown);
242
+ constructor(validator: Validator, schema: Schema | unknown, value: unknown, options?: SchemaRuntimeOptions);
222
243
  /**
223
244
  * Register a node as dependent on a path
224
245
  */
@@ -297,13 +318,43 @@ declare class SchemaRuntime {
297
318
  * runtime.getValue("/name"); // returns value at /name
298
319
  */
299
320
  getValue(path: string): unknown;
321
+ /**
322
+ * Internal helper to set a value at a normalized path.
323
+ * Handles both root and non-root paths.
324
+ *
325
+ * @param normalizedPath - The normalized JSON Pointer path
326
+ * @param value - The value to set
327
+ * @returns true if successful, false if the path cannot be set
328
+ */
329
+ private setValueAtPath;
330
+ /**
331
+ * Internal method to remove a value at a path.
332
+ * Shared logic for removeValue and setValue(undefined).
333
+ * @param path - The normalized path to remove
334
+ * @param canRemove - Whether the removal is allowed (pre-checked by caller)
335
+ * @returns true if successful, false if removal failed
336
+ */
337
+ private removeValueInternal;
300
338
  /**
301
339
  * Remove a node at the specified path.
302
340
  * This deletes the value from the data structure (array splice or object delete).
341
+ * After removal, may also remove empty parent containers based on removeEmptyContainers option.
303
342
  * @param path - The path to remove
304
343
  * @returns true if successful, false if the path cannot be removed
305
344
  */
306
345
  removeValue(path: string): boolean;
346
+ /**
347
+ * Clean up empty parent containers after element removal.
348
+ * Recursively removes empty arrays/objects based on removeEmptyContainers option.
349
+ * @param path - The path to check for empty container
350
+ * @returns The topmost parent path for reconciliation
351
+ */
352
+ private cleanupEmptyContainers;
353
+ /**
354
+ * Get default value for a schema, respecting autoFillDefaults option.
355
+ * Falls back to 'always' strategy if configured strategy returns undefined.
356
+ */
357
+ private getDefaultValueForAdd;
307
358
  /**
308
359
  * Add a new child to an array or object at the specified parent path.
309
360
  * For arrays, appends a new item with default value based on items schema.
@@ -319,13 +370,17 @@ declare class SchemaRuntime {
319
370
  * Creates intermediate containers (objects/arrays) as needed.
320
371
  * Triggers reconciliation and notifies subscribers.
321
372
  *
373
+ * When value is undefined and the field is not required, the field will be
374
+ * removed from the parent container (similar to removeValue behavior).
375
+ *
322
376
  * @param path - The JSON Pointer path (e.g., "/user/name", "" for root)
323
- * @param value - The new value to set
377
+ * @param value - The new value to set. If undefined and field is optional, removes the field.
324
378
  * @returns true if successful, false if the path cannot be set
325
379
  *
326
380
  * @example
327
381
  * runtime.setValue("/name", "Bob"); // set name to "Bob"
328
382
  * runtime.setValue("", { name: "Alice" }); // replace entire root value
383
+ * runtime.setValue("/optional", undefined); // remove optional field
329
384
  */
330
385
  setValue(path: string, value: unknown): boolean;
331
386
  /**
@@ -351,9 +406,14 @@ declare class SchemaRuntime {
351
406
  * This handles cases like if-then-else where new properties with defaults
352
407
  * may appear when conditions change.
353
408
  *
409
+ * Container initialization rules:
410
+ * - Root node is always considered required and will be initialized if it has defaults or required properties
411
+ * - Nested containers are initialized only if they are in parent's required array
412
+ *
354
413
  * @param instanceLocation - The path to the node
355
414
  * @param newSchema - The new effective schema
356
415
  * @param type - The schema type
416
+ * @param isRequired - Whether this node is required by its parent
357
417
  */
358
418
  private applySchemaDefaults;
359
419
  /**
@@ -365,6 +425,7 @@ declare class SchemaRuntime {
365
425
  * Build/update a FieldNode in place.
366
426
  * Updates the node's schema, type, error, and children based on the current value.
367
427
  * @param schema - Optional. If provided, updates node.originalSchema. Otherwise uses existing.
428
+ * @param isRequired - Whether this node is required by its parent schema.
368
429
  */
369
430
  private buildNode;
370
431
  }
@@ -439,19 +500,23 @@ declare function collectDependencies(schema: Schema, instanceLocation: string):
439
500
  */
440
501
  declare function extractReferencedPaths(conditionSchema: Schema, basePath?: string, depth?: number): string[];
441
502
 
503
+ interface GetDefaultValueOptions {
504
+ /** Optional existing value to fill defaults into */
505
+ value?: unknown;
506
+ /**
507
+ * Controls type-based default behavior:
508
+ * - 'always': Returns type-based defaults (e.g., "" for string, [] for array)
509
+ * - 'explicit' (default): Only returns explicitly declared schema.const or schema.default
510
+ */
511
+ strategy?: "always" | "explicit";
512
+ }
442
513
  /**
443
514
  * Generate a default value for a schema based on its type and constraints.
444
515
  *
445
516
  * Priority order for determining the default value:
446
517
  * 1. const - if defined, returns the const value
447
518
  * 2. default - if defined, returns the default value
448
- * 3. Type-based defaults:
449
- * - string: ""
450
- * - number/integer: 0
451
- * - boolean: false
452
- * - null: null
453
- * - object: {} with required properties recursively initialized
454
- * - array: []
519
+ * 3. Type-based defaults (controlled by strategy):
455
520
  *
456
521
  * For objects, only required properties are initialized with their default values.
457
522
  * If no type is specified but properties exist, the schema is treated as an object.
@@ -459,20 +524,21 @@ declare function extractReferencedPaths(conditionSchema: Schema, basePath?: stri
459
524
  * If a value is provided, it will be used as the base and missing defaults will be filled in.
460
525
  *
461
526
  * @param schema - The JSON Schema to generate a default value for
462
- * @param value - Optional existing value to fill defaults into
527
+ * @param options - Optional configuration for default value generation
463
528
  * @returns The generated default value, or undefined if type cannot be determined
464
529
  *
465
530
  * @example
466
- * getDefaultValue({ type: "string" }) // returns ""
531
+ * getDefaultValue({ type: "string" }) // returns undefined (explicit mode)
532
+ * getDefaultValue({ type: "string" }, { strategy: 'always' }) // returns ""
467
533
  * getDefaultValue({ type: "number", default: 42 }) // returns 42
468
534
  * getDefaultValue({ const: "fixed" }) // returns "fixed"
469
535
  * getDefaultValue({
470
536
  * type: "object",
471
537
  * properties: { name: { type: "string" } },
472
538
  * required: ["name"]
473
- * }) // returns { name: "" }
474
- * getDefaultValue({ type: "object", properties: { a: { default: 1 } } }, {}) // returns { a: 1 }
539
+ * }, { strategy: 'always' }) // returns { name: "" }
540
+ * getDefaultValue({ type: "object", properties: { a: { default: 1 } } }, { value: {} }) // returns { a: 1 }
475
541
  */
476
- declare function getDefaultValue(schema: Schema, value?: unknown): unknown;
542
+ declare function getDefaultValue(schema: Schema, options?: GetDefaultValueOptions): unknown;
477
543
 
478
- export { DRAFT_URIS, type ErrorFormatter, type ErrorMessage, type FieldNode, MESSAGES, type NormalizerOptions, type Output, type Schema, type SchemaChangeEvent, type SchemaDraft, SchemaRuntime, 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 };
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 };