@jarenjs/validate 0.9.2 → 0.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,13 +8,4 @@
8
8
  * @returns {function|undefined} The compiled validator function or undefined
9
9
  */
10
10
  export declare function compileDollarDataSchema(schemaObj: object, jsonSchema: object): Function | undefined;
11
- /**
12
- * Check if the schema has any $data references
13
- * This is used to determine if we should use the $data-aware compilation path
14
- * or the standard static compilation path.
15
- *
16
- * @param {object} jsonSchema - The JSON schema to check
17
- * @returns {boolean} True if the schema has any $data references
18
- */
19
- export declare function hasDollarDataReferences(jsonSchema: object): boolean;
20
11
  export declare function isDollarDataReference(jsonSchema: any): any;
@@ -1,6 +1,7 @@
1
1
  import { TraverseOptions } from './traverse.js';
2
2
  import { EvalLog } from './tools.js';
3
3
  export { registerFormatCompilers } from './format.js';
4
+ export { ValidationError, messagesEn, compileMessageTemplate, compileMessageCatalog, renderErrorMessage, localizeErrors, } from './messages.js';
4
5
  export { TraverseOptions };
5
6
  export type DollarDataRef = {
6
7
  /**
@@ -472,38 +473,6 @@ declare class InternalValidationError {
472
473
  rest: any;
473
474
  constructor(obj: any, key: any, expected: any, dataKey: any, value: any, rest: any);
474
475
  }
475
- /**
476
- * JSON Schema Validation Error
477
- * Represents a validation error according to the JSON Schema specification.
478
- * @see https://json-schema.org/draft/2020-12/json-schema-core.html#output
479
- */
480
- export declare class ValidationError {
481
- keyword: string;
482
- instancePath: string;
483
- schemaPath: string;
484
- params: object;
485
- message: string;
486
- /**
487
- * @param {object} options - Error options
488
- * @param {string} options.keyword - The keyword that failed validation
489
- * @param {string} options.instancePath - JSON Pointer to the data location
490
- * @param {string} options.schemaPath - JSON Pointer to the schema location
491
- * @param {object} options.params - Keyword-specific parameters
492
- * @param {string} [options.message] - Human-readable error message
493
- */
494
- constructor(options: {
495
- keyword: string;
496
- instancePath: string;
497
- schemaPath: string;
498
- params: object;
499
- message?: string;
500
- });
501
- /**
502
- * Convert error to a plain object
503
- * @returns {object} Plain object representation
504
- */
505
- toJSON(): object;
506
- }
507
476
  /**
508
477
  * ValidationOptions configures the behavior of the validation process.
509
478
  * @class
@@ -523,6 +492,10 @@ export declare class ValidationOptions {
523
492
  vocabValidation: boolean;
524
493
  /** @type {boolean|null} Whether the format keyword asserts (null = auto by draft) */
525
494
  formatAssertion: boolean | null;
495
+ /** @type {boolean} Whether collected errors carry rendered message text */
496
+ messages: boolean;
497
+ /** @type {'error'|'ignore'} What an asserting `format` with no registered compiler does */
498
+ unknownFormats: 'error' | 'ignore';
526
499
  /**
527
500
  * Creates validation options.
528
501
  * @param {boolean} [skipErrors=true] - Whether to stop at first error or continue
@@ -532,8 +505,10 @@ export declare class ValidationOptions {
532
505
  * @param {number} [draftVersion=7] - The JSON Schema draft version (6, 7, 2019, or 2020)
533
506
  * @param {boolean} [vocabValidation=true] - Whether the validation vocabulary is enabled (false when the schema's metaschema omits it via $vocabulary)
534
507
  * @param {boolean|null} [formatAssertion=null] - Whether format asserts (null = auto: asserts below draft 2020-12, annotation-only from 2020-12 on)
508
+ * @param {boolean} [messages=true] - Whether collected errors carry rendered message text; false skips rendering (message: '', params/msgid still set)
509
+ * @param {'error'|'ignore'} [unknownFormats='ignore'] - What to do when an ASSERTING `format` names something no compiler is registered for: 'ignore' (the default, and what the specification requires) accepts it as an annotation; 'error' throws at COMPILE time. Never affects instance validation, and never applies where format is annotation-only anyway.
535
510
  */
536
- constructor(skipErrors?: boolean, useGrapheme?: boolean, collectErrors?: boolean, contentValidation?: boolean | null, draftVersion?: number, vocabValidation?: boolean, formatAssertion?: boolean | null);
511
+ constructor(skipErrors?: boolean, useGrapheme?: boolean, collectErrors?: boolean, contentValidation?: boolean | null, draftVersion?: number, vocabValidation?: boolean, formatAssertion?: boolean | null, messages?: boolean, unknownFormats?: 'error' | 'ignore');
537
512
  }
538
513
  /**
539
514
  * ValidationRoot manages the compilation and validation context for a schema.
@@ -554,8 +529,6 @@ export declare class ValidationRoot {
554
529
  * `$ref`s resolve to the owner's `addSchema` registrations
555
530
  */
556
531
  constructor(origin: string, schemas: Map<any, any>, formats: Record<string, FormatCompiler>, opts?: ValidationOptions, traverse?: TraverseOptions, owner?: object | null);
557
- /** @returns {string} The root schema origin/URI */
558
- get rootOrigin(): string;
559
532
  /** @returns {TraverseOptions} Schema traversal options */
560
533
  get traverse(): TraverseOptions;
561
534
  /** @returns {ValidationOptions} Validation options */
@@ -564,8 +537,6 @@ export declare class ValidationRoot {
564
537
  get formats(): object;
565
538
  /** @returns {Array} Array of validation errors */
566
539
  get errors(): any[];
567
- /** @returns {ValidationObject} The root schema's ValidationObject */
568
- get firstSchema(): ValidationObject;
569
540
  /** @returns {boolean} Whether any schema in this compilation contains a $data reference */
570
541
  get usesDollarData(): boolean;
571
542
  /** @returns {boolean} Whether any schema in this compilation contains unevaluatedProperties/unevaluatedItems */
@@ -574,6 +545,16 @@ export declare class ValidationRoot {
574
545
  get evalLog(): EvalLog;
575
546
  /** @returns {object|null} The owning JarenValidator instance, or null when constructed standalone */
576
547
  get owner(): object | null;
548
+ /** @returns {Map<string, object>|null} Compiled 'errorMessage' specs by schema path, or null when the schema set has none */
549
+ get errorMessages(): Map<string, object> | null;
550
+ /**
551
+ * Register a compiled 'errorMessage' spec for a schema location.
552
+ * Called at schema compile time (see compileSchemaObject); the registry
553
+ * is only consulted at report time, over the already-failed set.
554
+ * @param {string} path - The schema path (ValidationObject.path)
555
+ * @param {object} spec - The compiled spec (see messages.js compileErrorMessageSpec)
556
+ */
557
+ registerErrorMessage(path: string, spec: object): void;
577
558
  /**
578
559
  * Creates a new ValidationObject for the given path and schema.
579
560
  * @param {string} path - The URI path for this schema object
@@ -588,18 +569,6 @@ export declare class ValidationRoot {
588
569
  * @returns {ValidationObject|null|undefined} The existing object, null if marked unresolved, or undefined if not known
589
570
  */
590
571
  unresolvedObject(path: string): ValidationObject | null | undefined;
591
- /**
592
- * Gets the raw schema object for a given reference without compiling it.
593
- * Used to check schema properties (like $recursiveAnchor) at compile time.
594
- * @param {string} ref - The reference URI to resolve
595
- * @param {string} path - The current path (for error messages)
596
- * @param {object} schema - The schema containing the $ref
597
- * @returns {{id: string, schema: object}|null} The resolved schema info or null
598
- */
599
- getRawSchema(ref: string, path: string, schema: object): {
600
- id: string;
601
- schema: object;
602
- } | null;
603
572
  /**
604
573
  * Gets the raw schema object by its URI/ID directly from the schemas map.
605
574
  * This performs a direct lookup without following references.
@@ -621,12 +590,41 @@ export declare class ValidationRoot {
621
590
  * @returns {boolean} Always returns false for convenience in validators
622
591
  */
623
592
  addError(error: InternalValidationError): boolean;
593
+ /**
594
+ * A checkpoint in the collected-error list.
595
+ *
596
+ * A SPECULATIVE applicator - an `anyOf` branch, an `if` condition, the
597
+ * subschema of a `not`, a `contains` candidate - runs a validator whose
598
+ * failure may be entirely expected. Those failures still call `addError`,
599
+ * so without a checkpoint they leak into the caller's issue list and blame
600
+ * a document for not matching a branch it was never required to match.
601
+ * Marking before the probe and rolling back after is the same discipline
602
+ * `EvalLog` already uses for annotations.
603
+ * @returns {number} The mark to pass to {@link rollbackErrors}
604
+ */
605
+ errorMark(): number;
606
+ /**
607
+ * Discard every error collected since `mark`.
608
+ * @param {number} mark - A value from {@link errorMark}
609
+ */
610
+ rollbackErrors(mark: number): void;
624
611
  /**
625
612
  * Validates data against the root schema.
626
613
  * @param {unknown} data - The data to validate
627
614
  * @returns {boolean} True if valid, false otherwise
628
615
  */
629
616
  validate(data: unknown): boolean;
617
+ /**
618
+ * Returns the fastest repeated-validation entry point for this root.
619
+ * Error collection and root-level dynamic anchors need the per-call
620
+ * bookkeeping of validate(); without them the compiled root validator
621
+ * only needs the annotation log cleared (when tracking is on) and can
622
+ * otherwise be invoked directly. Dynamic anchors pushed during
623
+ * validation are balanced by try/finally, so the anchor map needs no
624
+ * per-call clearing here.
625
+ * @returns {(data: unknown) => boolean} The validation entry point
626
+ */
627
+ createValidateFn(): (data: unknown) => boolean;
630
628
  /**
631
629
  * Get the stored validator for a dynamic anchor.
632
630
  * Used by $dynamicRef for runtime resolution.
@@ -702,8 +700,6 @@ export declare class ValidationObject {
702
700
  get path(): string;
703
701
  /** @returns {string} The effective base URI for resolving relative $refs */
704
702
  get baseUri(): string;
705
- /** @returns {Array} The current validation errors from the root */
706
- get errors(): any[];
707
703
  /** @returns {function} The compiled validator function */
708
704
  get validate(): Function;
709
705
  /** @returns {ValidationOptions} The validation options */
@@ -765,9 +761,103 @@ export declare class ValidatorOptions {
765
761
  */
766
762
  constructor(formats?: object | object[], schemas?: object[], validation?: ValidationOptions, traverse?: TraverseOptions);
767
763
  }
764
+ export type ValidationResultObject = {
765
+ valid: boolean;
766
+ errors: import("./messages.js").ValidationError[];
767
+ };
768
+ export type CompiledPredicate<T> = (data: unknown) => data is T;
769
+ export type CompiledCollector = (data: unknown) => ValidationResultObject;
770
+ export type ValidatorInit<TCollect extends boolean = false> = {
771
+ /**
772
+ * - Format compilers to register
773
+ */
774
+ formats?: Record<string, FormatCompiler>;
775
+ /**
776
+ * - Schemas to register
777
+ */
778
+ schemas?: (JSONSchema | boolean)[];
779
+ /**
780
+ * - Validation behavior options
781
+ */
782
+ validation?: ValidationOptions;
783
+ /**
784
+ * - Schema traversal options
785
+ */
786
+ traverse?: TraverseOptions;
787
+ /**
788
+ * - Return `{ valid, errors }` instead of a boolean
789
+ */
790
+ collectErrors?: TCollect;
791
+ /**
792
+ * - Stop at the first failure (defaults to `!collectErrors`)
793
+ */
794
+ skipErrors?: boolean;
795
+ /**
796
+ * - Count grapheme clusters for string length
797
+ */
798
+ useGrapheme?: boolean;
799
+ /**
800
+ * - Assert contentEncoding/contentMediaType
801
+ */
802
+ contentValidation?: boolean;
803
+ /**
804
+ * - The JSON Schema draft version
805
+ */
806
+ draftVersion?: number;
807
+ /**
808
+ * - Assert the format keyword
809
+ */
810
+ formatAssertion?: boolean;
811
+ /**
812
+ * - Render English message text on collected errors
813
+ */
814
+ messages?: boolean;
815
+ /**
816
+ * - What an ASSERTING `format` with no registered compiler does: 'ignore' (default, per spec) accepts it as an annotation, 'error' throws at compile time
817
+ */
818
+ unknownFormats?: 'error' | 'ignore';
819
+ };
820
+ /**
821
+ * The object a compiled validator returns when `collectErrors` is enabled.
822
+ * @typedef {{ valid: boolean, errors: import("./messages.js").ValidationError[] }} ValidationResultObject
823
+ */
824
+ /**
825
+ * A compiled validator in the default boolean mode. It is a type guard, so
826
+ * `T` is whatever the caller asserts the schema describes; with no `T` it
827
+ * behaves as an ordinary boolean predicate.
828
+ * @template T
829
+ * @typedef {(data: unknown) => data is T} CompiledPredicate
830
+ */
831
+ /**
832
+ * A compiled validator in collect-errors mode.
833
+ * @typedef {(data: unknown) => ValidationResultObject} CompiledCollector
834
+ */
835
+ /**
836
+ * The plain-object form accepted by the JarenValidator constructor, mixing
837
+ * validator-level settings with the ValidationOptions fields.
838
+ * @template {boolean} [TCollect=false]
839
+ * @typedef {object} ValidatorInit
840
+ * @property {Record<string, FormatCompiler>} [formats] - Format compilers to register
841
+ * @property {(JSONSchema | boolean)[]} [schemas] - Schemas to register
842
+ * @property {ValidationOptions} [validation] - Validation behavior options
843
+ * @property {TraverseOptions} [traverse] - Schema traversal options
844
+ * @property {TCollect} [collectErrors] - Return `{ valid, errors }` instead of a boolean
845
+ * @property {boolean} [skipErrors] - Stop at the first failure (defaults to `!collectErrors`)
846
+ * @property {boolean} [useGrapheme] - Count grapheme clusters for string length
847
+ * @property {boolean} [contentValidation] - Assert contentEncoding/contentMediaType
848
+ * @property {number} [draftVersion] - The JSON Schema draft version
849
+ * @property {boolean} [formatAssertion] - Assert the format keyword
850
+ * @property {boolean} [messages] - Render English message text on collected errors
851
+ * @property {'error'|'ignore'} [unknownFormats] - What an ASSERTING `format` with no registered compiler does: 'ignore' (default, per spec) accepts it as an annotation, 'error' throws at compile time
852
+ */
768
853
  /**
769
854
  * JarenValidator is the main entry point for JSON Schema validation.
770
855
  * It manages schema registration, format registration, and compilation.
856
+ *
857
+ * The `collectErrors` option decides what a compiled validator returns, and
858
+ * it is carried in the type parameter so the two shapes never have to be
859
+ * distinguished at runtime.
860
+ * @template {boolean} [TCollect=false]
771
861
  * @class
772
862
  * @example
773
863
  * const validator = new JarenValidator();
@@ -775,37 +865,37 @@ export declare class ValidatorOptions {
775
865
  * const validate = validator.compile({ $ref: 'http://example.com/schema' });
776
866
  * const valid = validate({ foo: 'bar' }); // true
777
867
  */
778
- export declare class JarenValidator {
868
+ export declare class JarenValidator<TCollect extends boolean = false> {
779
869
  #private;
780
870
  /**
781
871
  * Creates a new JarenValidator instance.
782
- * @param {ValidatorOptions} [options] - Validator options including formats, schemas, validation options, and traverse options
872
+ * @param {ValidatorOptions | ValidatorInit<TCollect>} [options] - Validator options including formats, schemas, validation options, and traverse options
783
873
  */
784
- constructor(options?: ValidatorOptions);
874
+ constructor(options?: ValidatorOptions | ValidatorInit<TCollect>);
785
875
  /**
786
876
  * Adds a format validator.
787
877
  * @param {string} name - The format name (e.g., 'email', 'uri', 'date-time')
788
878
  * @param {FormatCompiler} formatCompiler - A function that compiles format validators
789
- * @returns {JarenValidator} This validator instance for chaining
879
+ * @returns {this} This validator instance for chaining (the polymorphic `this` keeps the collectErrors type parameter across a chain)
790
880
  * @example
791
881
  * validator.addFormat('custom', (schemaObj, schema) => {
792
882
  * return (data) => data.startsWith('custom:');
793
883
  * });
794
884
  */
795
- addFormat(name: string, formatCompiler: FormatCompiler): JarenValidator;
885
+ addFormat(name: string, formatCompiler: FormatCompiler): this;
796
886
  /**
797
887
  * Adds multiple format validators at once.
798
888
  * @param {Record<string, FormatCompiler>} formatCompilers - Object mapping format names to compiler functions
799
- * @returns {JarenValidator} This validator instance for chaining
889
+ * @returns {this} This validator instance for chaining (the polymorphic `this` keeps the collectErrors type parameter across a chain)
800
890
  */
801
- addFormats(formatCompilers: Record<string, FormatCompiler>): JarenValidator;
891
+ addFormats(formatCompilers: Record<string, FormatCompiler>): this;
802
892
  /**
803
893
  * Adds schema(s) to the validator instance.
804
894
  * This method does not compile schemas - it only registers them for reference.
805
895
  * Dependencies can be added in any order, and circular dependencies are supported.
806
896
  * @param {JSONSchema | boolean | (JSONSchema | boolean)[]} schema - The schema(s) to add
807
897
  * @param {string} [key] - Optional key/URI to register the schema under
808
- * @returns {JarenValidator} This validator instance for chaining
898
+ * @returns {this} This validator instance for chaining (the polymorphic `this` keeps the collectErrors type parameter across a chain)
809
899
  * @example
810
900
  * // Add a single schema
811
901
  * validator.addSchema({ $id: 'http://example.com/user', type: 'object' });
@@ -816,18 +906,18 @@ export declare class JarenValidator {
816
906
  * // Add with explicit key
817
907
  * validator.addSchema({ type: 'string' }, 'http://example.com/name');
818
908
  */
819
- addSchema(schema: JSONSchema | boolean | (JSONSchema | boolean)[], key?: string): JarenValidator;
909
+ addSchema(schema: JSONSchema | boolean | (JSONSchema | boolean)[], key?: string): this;
820
910
  static normalizeUriKey(key: any): any;
821
911
  /**
822
912
  * Adds meta-schema(s) that can be used to validate schemas.
823
913
  * Meta-schemas are schemas that describe the structure of valid JSON schemas.
824
914
  * @param {JSONSchema | boolean | (JSONSchema | boolean)[]} schema - The meta-schema(s) to add
825
915
  * @param {string} [key] - Optional key/URI for the meta-schema
826
- * @returns {JarenValidator} This validator instance for chaining
916
+ * @returns {this} This validator instance for chaining (the polymorphic `this` keeps the collectErrors type parameter across a chain)
827
917
  * @example
828
918
  * validator.addMetaSchema(draft7MetaSchema, 'http://json-schema.org/draft-07/schema');
829
919
  */
830
- addMetaSchema(schema: JSONSchema | boolean | (JSONSchema | boolean)[], key?: string): JarenValidator;
920
+ addMetaSchema(schema: JSONSchema | boolean | (JSONSchema | boolean)[], key?: string): this;
831
921
  /**
832
922
  * Retrieves a registered schema by its key/URI.
833
923
  * @param {string} key - The schema URI/key
@@ -848,9 +938,16 @@ export declare class JarenValidator {
848
938
  * Compiles a schema into a validation function.
849
939
  * This is the main method for creating validators. It resolves all $ref references,
850
940
  * compiles the schema structure, and returns a function that validates data.
941
+ * The return type follows the instance's `collectErrors` setting: a type
942
+ * guard over `unknown` by default, or a function producing
943
+ * `{ valid, errors }` when errors are collected. Jaren does not infer `T`
944
+ * from the schema — the caller asserts what the schema describes, which is
945
+ * what a checked contract wrapper wants; pair it with a schema-to-type
946
+ * generator if you need the shape derived mechanically.
947
+ * @template [T=unknown]
851
948
  * @param {JSONSchema | boolean} schema - The schema to compile
852
949
  * @param {(JSONSchema | boolean)[]} [schemas] - Additional schemas to reference during compilation
853
- * @returns {(data: any) => boolean | {valid: boolean, errors: ValidationError[]}} A validation function
950
+ * @returns {TCollect extends true ? CompiledCollector : CompiledPredicate<T>} A validation function
854
951
  * @example
855
952
  * const validate = validator.compile({
856
953
  * type: 'object',
@@ -862,13 +959,14 @@ export declare class JarenValidator {
862
959
  * const valid = validate({ name: 'John' }); // true
863
960
  * const invalid = validate({ name: 123 }); // false
864
961
  *
962
+ * // Narrowing to a caller-asserted type
963
+ * const isUser = validator.compile<{ name: string }>(userSchema);
964
+ * if (isUser(input)) input.name; // input is { name: string } here
965
+ *
865
966
  * // With error collection
866
- * validator = new JarenValidator({ collectErrors: true });
867
- * const result = validate({ name: 123 });
967
+ * const collecting = new JarenValidator({ collectErrors: true });
968
+ * const result = collecting.compile(schema)({ name: 123 });
868
969
  * // result = { valid: false, errors: [...] }
869
970
  */
870
- compile(schema: JSONSchema | boolean, schemas?: (JSONSchema | boolean)[]): (data: any) => boolean | {
871
- valid: boolean;
872
- errors: ValidationError[];
873
- };
971
+ compile<T = unknown>(schema: JSONSchema | boolean, schemas?: (JSONSchema | boolean)[]): TCollect extends true ? CompiledCollector : CompiledPredicate<T>;
874
972
  }
@@ -0,0 +1,142 @@
1
+ export { compileMessageTemplate, compileMessageCatalog, } from '@jarenjs/core/message';
2
+ /**
3
+ * The built-in English catalog: one entry per message key the validator
4
+ * produces, rendering the exact strings of the historical `#convertErrors`
5
+ * if/else chain. Keywords without an entry (`const`, `enum`,
6
+ * `dependentRequired`, ...) fall back to the generic
7
+ * `validation failed for keyword '<keyword>'` - as before.
8
+ * @type {Record<string, string | ((params: object, error?: object) => string)>}
9
+ */
10
+ export declare const messagesEn: Record<string, string | ((params: object, error?: object) => string)>;
11
+ /**
12
+ * JSON Schema Validation Error
13
+ * Represents a validation error according to the JSON Schema specification.
14
+ * @see https://json-schema.org/draft/2020-12/json-schema-core.html#output
15
+ */
16
+ export declare class ValidationError {
17
+ keyword: string;
18
+ instancePath: string;
19
+ schemaPath: string;
20
+ params: object;
21
+ msgid: string;
22
+ message: string;
23
+ /**
24
+ * @param {object} options - Error options
25
+ * @param {string} options.keyword - The keyword that failed validation
26
+ * @param {string} options.instancePath - JSON Pointer to the data location
27
+ * @param {string} options.schemaPath - JSON Pointer to the schema location
28
+ * @param {object} options.params - Keyword-specific parameters
29
+ * @param {string} [options.msgid] - Stable message key resolving this error in a catalog
30
+ * @param {string} [options.message] - Human-readable error message
31
+ */
32
+ constructor(options: {
33
+ keyword: string;
34
+ instancePath: string;
35
+ schemaPath: string;
36
+ params: object;
37
+ msgid?: string;
38
+ message?: string;
39
+ });
40
+ /**
41
+ * Convert error to a plain object
42
+ * @returns {object} Plain object representation
43
+ */
44
+ toJSON(): object;
45
+ }
46
+ /**
47
+ * Render the message of an error through a catalog - the tail of the
48
+ * resolution precedence chain (no `errorMessage` registry involvement):
49
+ * catalog[msgid], built-in English[msgid], catalog[keyword],
50
+ * built-in English[keyword], then the generic fallback text.
51
+ * @param {ValidationError | {keyword: string, msgid?: string, params?: object}} error - The error to render
52
+ * @param {Readonly<Record<string, (params: object, error?: object) => string>>} [catalog] - A compiled catalog (see {@link compileMessageCatalog})
53
+ * @returns {string} The rendered message
54
+ */
55
+ export declare function renderErrorMessage(error: ValidationError | {
56
+ keyword: string;
57
+ msgid?: string;
58
+ params?: object;
59
+ }, catalog?: Readonly<Record<string, (params: object, error?: object) => string>>): string;
60
+ /**
61
+ * Re-render the `message` of every error from its `msgid` + `params`
62
+ * through the given catalog, with built-in English fallback. This is the
63
+ * whole post-hoc i18n story:
64
+ * `localizeErrors(validate(data).errors, compileMessageCatalog(nl))`.
65
+ *
66
+ * Inline schema-authored messages (a MessageSpec without `$msgid`) are
67
+ * single-language by definition and are NOT re-rendered - that is why
68
+ * `$msgid` exists. An error whose `msgid` resolves in no catalog keeps
69
+ * its current message (e.g. the spec's inline fallback text).
70
+ * @param {ValidationError[]} errors - Errors from a collect-mode validation
71
+ * @param {Readonly<Record<string, (params: object, error?: object) => string>>} catalog - A compiled catalog (see {@link compileMessageCatalog})
72
+ * @returns {ValidationError[]} The same array, messages re-rendered
73
+ */
74
+ export declare function localizeErrors(errors: ValidationError[], catalog: Readonly<Record<string, (params: object, error?: object) => string>>): ValidationError[];
75
+ export type CompiledMessageSpec = {
76
+ /**
77
+ * - Catalog key to resolve at render time
78
+ */
79
+ msgid: string | null;
80
+ /**
81
+ * - Compiled inline template
82
+ */
83
+ render: ((params: object, error?: object) => string) | null;
84
+ /**
85
+ * - Author params, merged OVER the error's params
86
+ */
87
+ params: object | null;
88
+ };
89
+ export type CompiledErrorMessageNode = {
90
+ /**
91
+ * - String-form spec: covers this node AND its subtree
92
+ */
93
+ all: CompiledMessageSpec | null;
94
+ /**
95
+ * - Map-form per-keyword specs (this node only)
96
+ */
97
+ keywords: Map<string, CompiledMessageSpec | {
98
+ perKey: Map<string, CompiledMessageSpec>;
99
+ fallback: CompiledMessageSpec | null;
100
+ }> | null;
101
+ /**
102
+ * - The '_' entry (this node only)
103
+ */
104
+ catchAll: CompiledMessageSpec | null;
105
+ };
106
+ /**
107
+ * A compiled 'errorMessage' node registered on the ValidationRoot.
108
+ * @typedef {object} CompiledErrorMessageNode
109
+ * @property {CompiledMessageSpec|null} all - String-form spec: covers this node AND its subtree
110
+ * @property {Map<string, CompiledMessageSpec | {perKey: Map<string, CompiledMessageSpec>, fallback: CompiledMessageSpec|null}>|null} keywords - Map-form per-keyword specs (this node only)
111
+ * @property {CompiledMessageSpec|null} catchAll - The '_' entry (this node only)
112
+ */
113
+ /**
114
+ * Compile the value of an 'errorMessage' keyword into a registry node.
115
+ * Grammar (validated here, at schema compile time):
116
+ * - MessageSpec (string / `$msgid` object): covers the whole subtree;
117
+ * - map form: per-keyword MessageSpecs for this node, where `required`
118
+ * also accepts a per-missing-property map, `$query` a per-runtime-code
119
+ * map (with `default` for the EBV-false failure), and `_` is the
120
+ * node-level catch-all.
121
+ * @param {unknown} errorMessage - The keyword's value
122
+ * @param {string} path - The schema path, for compile error messages
123
+ * @returns {CompiledErrorMessageNode} The compiled node
124
+ */
125
+ export declare function compileErrorMessageSpec(errorMessage: unknown, path: string): CompiledErrorMessageNode;
126
+ /**
127
+ * Convert internal validation errors to the public ValidationError format.
128
+ * Params extraction is table-driven off the failed keyword; message text
129
+ * goes through the errorMessage registry (if any) and the built-in
130
+ * English catalog. With `options.messages === false` no message is
131
+ * rendered at all (`message: ''`, params and msgid still set).
132
+ * @param {Array<{object: any, key: string|string[], expected: any, dataKey: any, value: any, rest: any[]}>} internalErrors - The root's internal error records
133
+ * @returns {ValidationError[]} The public errors
134
+ */
135
+ export declare function convertInternalErrors(internalErrors: Array<{
136
+ object: any;
137
+ key: string | string[];
138
+ expected: any;
139
+ dataKey: any;
140
+ value: any;
141
+ rest: any[];
142
+ }>): ValidationError[];
@@ -0,0 +1,107 @@
1
+ export type Normalizer<In = unknown, Out = In> = (data: In) => Out;
2
+ export type NormalizeOptions = {
3
+ /**
4
+ * - Materialize `default` for absent object properties, recursively
5
+ */
6
+ useDefaults?: boolean | ((schemaNode: Record<string, unknown>) => boolean);
7
+ /**
8
+ * - Strip unknown properties: `true` only where `additionalProperties: false`, `'all'` wherever an object shape is declared
9
+ */
10
+ removeAdditional?: boolean | 'all';
11
+ /**
12
+ * - Convert a value to the node's declared scalar `type` when it is convertible
13
+ */
14
+ coerceTypes?: boolean | ((schemaNode: Record<string, unknown>) => boolean);
15
+ /**
16
+ * - Trim leading/trailing whitespace from strings, before coercion
17
+ */
18
+ trimStrings?: boolean | ((schemaNode: Record<string, unknown>) => boolean);
19
+ };
20
+ /**
21
+ * Collect the `$anchor` declarations of one schema document: a map from
22
+ * anchor name to the schema node that declares it. Exported because
23
+ * `@jarenjs/emit` resolves the same references when it derives types, and two
24
+ * walks with different scope rules would make a generated type disagree with
25
+ * this normalizer — the one defect class that package must not have.
26
+ *
27
+ * The scope is the same-document scope the rest of this module uses: a
28
+ * subtree that declares its own `$id` is an embedded resource with its own
29
+ * anchor scope, so it is not descended. First declaration wins, which keeps
30
+ * the map deterministic for a document that (invalidly) repeats a name.
31
+ * @param {object|boolean} root - The root schema of the document
32
+ * @returns {Map<string, object>} anchor name -> schema node
33
+ */
34
+ export declare function collectSameDocumentAnchors(root: object | boolean): Map<string, object>;
35
+ /**
36
+ * Resolve a same-document `$ref` — `#`, `#/` followed by a JSON Pointer, or
37
+ * `#name` for a plain `$anchor` — to the schema it addresses. Refs into other
38
+ * documents are not followed: a normalizer compiles one schema, and reaching
39
+ * a registered sibling would mean owning the whole resolution scope that
40
+ * `compile` owns. Exported for `@jarenjs/emit`, which must resolve references
41
+ * with exactly these rules when it derives the accepted/normalized variants.
42
+ * @param {string} ref - The reference
43
+ * @param {object|boolean} root - The root schema being compiled
44
+ * @param {Map<string, object>} [anchors] - The document's anchor map, from
45
+ * {@link collectSameDocumentAnchors}; omit to skip anchor resolution
46
+ * @returns {object|boolean|undefined} The addressed schema, or undefined
47
+ */
48
+ export declare function resolveSameDocumentRef(ref: string, root: object | boolean, anchors?: Map<string, object>): object | boolean | undefined;
49
+ /**
50
+ * Resolve a per-node normalization switch at COMPILE time. Exported because
51
+ * `@jarenjs/emit` has to answer the same question when it derives the accepted
52
+ * and normalized type variants: two implementations of this rule would drift,
53
+ * and a type that disagrees with the normalizer is worse than no type. `true` turns the
54
+ * behavior on everywhere, `false` nowhere, and a predicate decides per schema
55
+ * node — which is how a consumer expresses "trim these 34 string fields, not
56
+ * the other 185" without the option becoming a whole-schema blunt instrument.
57
+ * Because it runs during compilation, a predicate costs nothing at runtime.
58
+ * @param {boolean|((node: Record<string, unknown>) => boolean)|undefined} option
59
+ * @param {object} node - The schema node the switch applies to
60
+ * @returns {boolean}
61
+ */
62
+ export declare function resolveNormalizeSwitch(option: boolean | ((node: Record<string, unknown>) => boolean) | undefined, node: object): boolean;
63
+ /**
64
+ * Compile a JSON Schema into a normalizer: a function that returns a
65
+ * normalized copy of its input, leaving the input untouched.
66
+ *
67
+ * Validation is unaffected and unchanged - normalize first, then hand the
68
+ * result to a compiled validator:
69
+ *
70
+ * ```javascript
71
+ * const normalize = compileNormalizer(schema, { useDefaults: true, trimStrings: true });
72
+ * const validate = new JarenValidator({ collectErrors: true }).compile(schema);
73
+ * const shaped = normalize(input);
74
+ * const result = validate(shaped);
75
+ * ```
76
+ *
77
+ * **What is normalized.** `properties`, `patternProperties`,
78
+ * `additionalProperties`, `items`/`prefixItems`/`additionalItems`, same-document
79
+ * `$ref` (`#`, `#/pointer` and plain `#anchor` forms), and `allOf` (composed,
80
+ * with stripping disabled inside it).
81
+ *
82
+ * **What is not, and why.** `anyOf`, `oneOf`, `if`/`then`/`else` and `not`
83
+ * are not descended: which branch applies is only known after validating,
84
+ * and normalizing under a branch can change which branch validates. Nothing
85
+ * arbitrary runs either - there is no transform hook, because an arbitrary
86
+ * transform is application code, not schema semantics, and belongs on the
87
+ * caller's side of the boundary.
88
+ * @template [In=unknown]
89
+ * @template [Out=In]
90
+ * @param {object|boolean} schema - The schema to compile
91
+ * @param {NormalizeOptions} [options] - Which normalizations to apply
92
+ * @returns {Normalizer<In, Out>} The compiled normalizer
93
+ * @example
94
+ * const normalize = compileNormalizer({
95
+ * type: 'object',
96
+ * properties: {
97
+ * name: { type: 'string' },
98
+ * port: { type: 'integer', default: 8080 },
99
+ * },
100
+ * additionalProperties: false,
101
+ * }, { useDefaults: true, removeAdditional: true, coerceTypes: true, trimStrings: true });
102
+ *
103
+ * const input = { name: ' jaren ', port: '9000', stray: 1 };
104
+ * normalize(input); // { name: 'jaren', port: 9000 }
105
+ * input; // { name: ' jaren ', port: '9000', stray: 1 } - untouched
106
+ */
107
+ export declare function compileNormalizer<In = unknown, Out = In>(schema: object | boolean, options?: NormalizeOptions): Normalizer<In, Out>;