@jarenjs/formats 0.9.2 → 0.34.0

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/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @jarenjs/formats
2
2
 
3
- Format validators for the JSON Schema `format` keyword, built on the text validators of [`@jarenjs/core`](../core). Includes all standard string formats (`date-time`, `date`, `time`, `duration`, `email`, `idn-email`, `hostname`, `idn-hostname`, `ipv4`, `ipv6`, `uri`, `uri-reference`, `uri-template`, `iri`, `iri-reference`, `uuid`, `regex`) plus many extras (`isbn10`, `mac`, `base64`, `alpha`, `color`, ...) and numeric formats (`int8` ... `uint64`, `float16` ... `float64`).
3
+ Format validators for the JSON Schema `format` keyword, built on the text validators of [`@jarenjs/core`](../core). Includes all standard string formats (`date-time`, `date`, `time`, `duration`, `email`, `idn-email`, `hostname`, `idn-hostname`, `ipv4`, `ipv6`, `uri`, `uri-reference`, `uri-template`, `iri`, `iri-reference`, `uuid`, `regex`) plus many extras (`iregexp`, `isbn10`, `mac`, `base64`, `alpha`, `color`, ...) and numeric formats (`int8` ... `uint64`, `float16` ... `float64`).
4
4
 
5
- The JSON addressing formats are grouped separately in `jsonFormats`: `json-pointer`, `json-pointer-uri-fragment` and `relative-json-pointer` (RFC 6901), and `json-path`, which validates query strings against the complete [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html) grammar using the parser of the JSONPath compiler in `@jarenjs/json`.
5
+ The JSON addressing formats are grouped separately in `jsonFormats`: `json-pointer`, `json-pointer-uri-fragment` and `relative-json-pointer` (RFC 6901), and `json-path`, which validates query strings against the complete [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html) grammar using the parser of the JSONPath compiler in `@jarenjs/json`, plus `json-path-segments` for the variable-rooted path strings of the Jaren query format.
6
6
 
7
- The name predicate bindings live in one canonical table, exported as `formatTesters` (plus the per-group `stringFormatTesters`, `jsonFormatTesters`, `dateTimeFormatTesters`, `numberFormatTesters`): bare synchronous predicates without validator coupling. The format compilers above wrap these testers in the validator contract, and [`@jarenjs/forms`](../forms) merges its rendering hints over the same table for per-keystroke field validation — one registry, so the two layers can never drift apart.
7
+ The geospatial formats are grouped in `geoFormats`: `geohash`, `wkt` and `geojson`, backed by the spatial kernel in `@jarenjs/core/geo`.
8
+
9
+ The name → predicate bindings live in one canonical table, exported as `formatTesters` (plus the per-group `stringFormatTesters`, `jsonFormatTesters`, `geoFormatTesters`, `dateTimeFormatTesters`, `numberFormatTesters`): bare synchronous predicates without validator coupling. The format compilers above wrap these testers in the validator contract, and [`@jarenjs/forms`](../forms) merges its rendering hints over the same table for per-keystroke field validation — one registry, so the two layers can never drift apart.
8
10
 
9
11
  ## Usage
10
12
 
@@ -16,7 +18,8 @@ const jaren = new JarenValidator()
16
18
  .addFormats(formats.stringFormats)
17
19
  .addFormats(formats.numberFormats)
18
20
  .addFormats(formats.dateTimeFormats)
19
- .addFormats(formats.jsonFormats);
21
+ .addFormats(formats.jsonFormats)
22
+ .addFormats(formats.geoFormats);
20
23
 
21
24
  const validate = jaren.compile({ type: 'string', format: 'json-path' });
22
25
  validate('$.store.book[?@.price < 10]'); // true
@@ -24,6 +27,10 @@ validate('$.store.book[?@.price < 10]'); // true
24
27
 
25
28
  Format assertion follows the specification per draft: asserted through draft 2019-09, annotation-only from draft 2020-12 on unless enabled via the `formatAssertion` option (`new JarenValidator({ formatAssertion: true })`) or a metaschema that declares the `format-assertion` vocabulary.
26
29
 
30
+ **Register the group before you use a name from it, and register the right one.** The groups are split, so `date-time` lives in `dateTimeFormats` and *not* in `stringFormats`, and `json-path` lives in `jsonFormats`. Registering only `stringFormats` and then writing `format: 'date-time'` leaves the keyword accepting every value — per spec, an unregistered format is an annotation and asserts nothing, so nothing anywhere reports it. For schemas you own, compile with `new JarenValidator({ unknownFormats: 'error' })`: the missing registration then fails at compile time instead of silently. This repository does that for every schema it ships, gated by `test/validate/our-schema-formats.test.js`.
31
+
32
+ **Register the compilers, not the testers.** `stringFormats` and `formatTesters` are both objects full of functions, but only the *compilers* take `(schemaObj, jsonSchema)` and return the per-value validator; a tester registered in a compiler's place compiles to nothing. That one throws under either `unknownFormats` setting, because it is never intentional. Use `formatTesters` directly — as [`@jarenjs/forms`](../forms) does for per-keystroke field validation — rather than through `addFormats`.
33
+
27
34
  ## ✍ The complete format list
28
35
 
29
36
  ### ✍ Formats for strings
@@ -38,24 +45,20 @@ These format validators are based on the [json-schema.org](https://json-schema.o
38
45
 
39
46
  - `duration` | duration from RFC3339
40
47
  - `iso-date-time` | ISO 8601 date-time with optional timezone
41
- - `iso-time` | ISO 8601 time with optional timezone
48
+ - `iso-time` | ISO 8601 time with optional timezone — the timezone offset is uniformly optional, so a zone-less time such as `12:30:00` validates
42
49
 
43
- *Note: All date time formats can use formatMinimum / formatMaximum and formatExclusiveMinimum and formatExclusiveMaximum*
50
+ *Note: All date time formats can use formatMinimum / formatMaximum and formatExclusiveMinimum and formatExclusiveMaximum. The bounds are folded to epoch milliseconds at compile time and string values compare as numbers, so a validation allocates no `Date`; a raw `Date` instance as the value is still accepted and compares numerically.*
44
51
 
45
52
  #### 🗨 Formats for url's, hostnames and emails
46
53
 
47
- - `url` | full URL
48
- - `url--full` | same as `url`, but more comprehensive
49
- - `uri` | full URI
50
- - `uri--full` | same as `uri`, but more comprehensive
51
- - `uri-reference` | URI reference, including full and relative URIs
52
- - `uri-reference--full` | same as `uri-reference`, but more comprehensive
54
+ - `url` | http/https URL — a `uri` narrowed to the web schemes, so it must carry an authority and the RFC 3986 grammar still applies (`http://localhost:8080` and `http://127.0.0.1/` are URLs; `http://x/a|b` is not)
55
+ - `uri` | full URI according to [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986), parsed against the grammar by character code — an ASCII grammar throughout, so a string carrying non-ASCII characters is an `iri` and not a `uri`
56
+ - `uri-reference` | URI reference, absolute or relative, according to [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986)
53
57
  - `uri-template` | URI template according to [RFC6570](https://datatracker.ietf.org/doc/html/rfc6570)
54
- - `iri` | full URI with international characters
55
- - `iri-reference` | full URI reference with with international characters
58
+ - `iri` | full URI with international characters, according to [RFC3987](https://datatracker.ietf.org/doc/html/rfc3987) — parsed against the grammar by character code, so percent-encoding must be well formed and `iprivate` is accepted in the query only
59
+ - `iri-reference` | full IRI reference, absolute or relative, according to [RFC3987](https://datatracker.ietf.org/doc/html/rfc3987)
56
60
 
57
- - `email` | email address
58
- - `email--full` | same as email, but more comprehensive
61
+ - `email` | email address according to [RFC5321](https://datatracker.ietf.org/doc/html/rfc5321), including quoted-string local parts and `[192.0.2.1]` / `[IPv6:::1]` address literals
59
62
  - `hostname` | host name according to [RFC1034](https://datatracker.ietf.org/doc/html/rfc1034#section-3.5)
60
63
  - `idn-hostname` | host name with international characters
61
64
  - `idn-email` | email address with international characters
@@ -81,6 +84,7 @@ These are grouped in `jsonFormats`.
81
84
  - `json-pointer-uri-fragment` | JSON-pointer fragment according to [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901#section-6)
82
85
  - `relative-json-pointer` | relative JSON-pointer according to [draft-luff-relative-json-pointer-00](https://datatracker.ietf.org/doc/html/draft-luff-relative-json-pointer-00)
83
86
  - `json-path` | JSONPath query according to [RFC9535](https://www.rfc-editor.org/rfc/rfc9535.html), checked against the complete grammar (including filter well-typedness) by the parser of the JSONPath compiler in `@jarenjs/json`
87
+ - `json-path-segments` | a variable-rooted path string — `$name` followed by optional [RFC9535](https://www.rfc-editor.org/rfc/rfc9535.html) segments (`$book.price[?@.isbn]`), the form the Jaren query format uses to address a bound variable. Not a valid RFC 9535 query on its own (the RFC's root identifier is `$` alone), so `json-path` rejects it; both formats recognize the five built-in function extensions and no others, because a format has to mean the same thing in every schema
84
88
 
85
89
  #### 🗨 Miscellaneous formats
86
90
 
@@ -92,14 +96,25 @@ These are grouped in `jsonFormats`.
92
96
  - `lowercase` | allow only lower case alpha characters
93
97
  - `color` | web color hex string (starts with #, must be 3 or 6 hax characters)
94
98
  - `regex` | tests whether a string is a valid regular expression
99
+ - `iregexp` | tests whether a string is a valid I-Regexp according to [RFC9485](https://www.rfc-editor.org/rfc/rfc9485.html) — the interoperable subset that means the same thing in every regexp dialect, so it is stricter than `regex`: shorthand classes (`\d`, `\w`), lazy quantifiers, anchors and lookaround are all rejected
95
100
  - `base64` | base64 encoded data
96
101
  - `byte` | same as `base64` format
97
102
 
98
103
  - `isbn10` | International Standard Book Number 10 digit number
99
104
  - `isbn13` | International Standard Book Number 13 digit number
100
105
 
101
- - `country2` | Country code by alpha-2 according to ISO3166-1 _!No tests exists!_
102
- - `iban` | International Bank Account Number _!No tests exists!_
106
+ - `country2` | country code by alpha-2 according to [ISO3166-1](https://www.iso.org/iso-3166-country-codes.html) the 249 assigned codes plus `XK`, the user-assigned code for Kosovo; matched case-insensitively
107
+ - `iban` | International Bank Account Number according to [ISO13616](https://www.iso.org/standard/81090.html) — checks the country's registered length, the alphanumeric body and the ISO 7064 MOD 97-10 check digits, so a transposed digit is caught; accepts both the compact electronic format (`NL91ABNA0417164300`) and the print format grouped in fours (`NL91 ABNA 0417 1643 00`)
108
+
109
+ ### ✍ Geospatial formats
110
+
111
+ These are grouped in `geoFormats`, backed by the spatial kernel in [`@jarenjs/core/geo`](../core).
112
+
113
+ - `geohash` | a base-32 geohash cell name, any length (`u173z`); the alphabet is lowercase and deliberately omits `a`, `i`, `l` and `o`
114
+ - `wkt` | a Well-Known Text geometry (ISO 19125 / OGC Simple Features): the seven tagged types with optional `Z`/`M`/`ZM` modifiers, `EMPTY`, consistent coordinate counts and closed polygon rings; an unmodified tag accepts 2 or 3 coordinates per point, as the field (PostGIS) does
115
+ - `geojson` | a structurally valid GeoJSON object per [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946) — unlike every other format this one applies to **objects**, and it enforces the invariant JSON Schema provably cannot: every linear ring closed
116
+
117
+ `geojson` exists *next to* the GeoJSON meta-schema artifacts in [`@jarenjs/json`](../json), not instead of them, and the division of labour is deliberate: the format is the one-keyword annotation that answers yes or no in a single call, while the meta-schema locates the failure and (in the `$query`-extended variant) also checks ring winding. Reach for the schema when you want a diagnosis; reach for the format when you only want the gate.
103
118
 
104
119
  ### ✍ Formats for numbers
105
120
 
@@ -126,4 +141,4 @@ These are grouped in `numberFormats`. Formats for numbers validate both numbers
126
141
 
127
142
  ## Development
128
143
 
129
- Unit tests live in `test/formats/` at the repository root; `test/formats/testers.test.js` enforces that every compiler registry's key set equals its tester group's, so the validator layer and the bare-predicate layer can never drift. The predicates themselves are implemented and tested in [`@jarenjs/core`](../core). See the repository [README](../../README.md) for the monorepo picture and the [ROADMAP](../../ROADMAP.md) for planned formats.
144
+ Unit tests live in `test/formats/` at the repository root; `test/formats/testers.test.js` enforces that every compiler registry's key set equals its tester group's, so the validator layer and the bare-predicate layer can never drift. The predicates themselves are implemented and tested in [`@jarenjs/core`](../core). See the repository [README](../../README.md) for the monorepo picture and the [ROADMAP](../../docs/ROADMAP.md) for planned formats.
@@ -0,0 +1,77 @@
1
+ export type JSONSchema = {
2
+ format?: string;
3
+ formatMinimum?: string;
4
+ formatExclusiveMinimum?: string;
5
+ formatMaximum?: string;
6
+ formatExclusiveMaximum?: string;
7
+ };
8
+ export type ValidationObject = {
9
+ options: {
10
+ skipErrors: boolean;
11
+ };
12
+ createErrorHandler: (expected: any, key: string, ...details: any[]) => (data: any, dataPath?: string) => boolean;
13
+ };
14
+ /**
15
+ * @typedef {{format?: string, formatMinimum?: string, formatExclusiveMinimum?: string, formatMaximum?: string, formatExclusiveMaximum?: string}} JSONSchema
16
+ * @typedef {{
17
+ * options: {skipErrors: boolean},
18
+ * createErrorHandler: (expected: any, key: string, ...details: any[]) => (data: any, dataPath?: string) => boolean
19
+ * }} ValidationObject
20
+ */
21
+ /**
22
+ * Compiles a validator for the 'geohash' format.
23
+ * Validates geohash strings: any length, every character in the
24
+ * base-32 geohash alphabet.
25
+ *
26
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
27
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
28
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
29
+ * @example
30
+ * compileGeohashFormat(schemaObj, { format: 'geohash' })('u173z'); // true
31
+ * compileGeohashFormat(schemaObj, { format: 'geohash' })('u17a'); // false ('a' is not in the alphabet)
32
+ */
33
+ export declare const compileGeohashFormat: (schemaObj: import("./string.js").ValidationObject, jsonSchema: import("./string.js").JSONSchema) => (data: unknown, dataPath?: string) => boolean;
34
+ /**
35
+ * Compiles a validator for the 'wkt' format.
36
+ * Validates Well-Known Text geometry strings (ISO 19125 / OGC Simple
37
+ * Features): the seven tagged geometry types with optional Z/M/ZM
38
+ * modifiers, consistent coordinate counts, and closed polygon rings.
39
+ *
40
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
41
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
42
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
43
+ * @example
44
+ * compileWktFormat(schemaObj, { format: 'wkt' })('POINT (4.9041 52.3676)'); // true
45
+ * compileWktFormat(schemaObj, { format: 'wkt' })('POLYGON ((0 0, 4 0, 4 4, 1 1))'); // false (open ring)
46
+ */
47
+ export declare const compileWktFormat: (schemaObj: import("./string.js").ValidationObject, jsonSchema: import("./string.js").JSONSchema) => (data: unknown, dataPath?: string) => boolean;
48
+ /**
49
+ * Compiles a validator for the 'geojson' format. Unlike the string
50
+ * formats, this one applies to **objects**: any non-array object must be
51
+ * a structurally valid GeoJSON object (RFC 7946) — the coordinate
52
+ * nesting its `type` requires, positions inside the WGS 84 bounds, and
53
+ * every linear ring closed. Non-object values pass, following the rule
54
+ * that a format constrains only its own type.
55
+ *
56
+ * This is the quick, shallow judgment; the GeoJSON meta-schema artifacts
57
+ * in `@jarenjs/json` validate the same grammar more thoroughly (locating
58
+ * the failure, and — in the Jaren-extended variant — checking ring
59
+ * winding through `$query`). Reach for the schema when you want to know
60
+ * *what* is wrong; reach for the format when a one-keyword annotation is
61
+ * worth more than a diagnosis.
62
+ *
63
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
64
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
65
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
66
+ * @example
67
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })({ type: 'Point', coordinates: [4.9, 52.4] }); // true
68
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })({ type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[2,2]]] }); // false (open ring)
69
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })('not an object'); // true (wrong type is not this format's business)
70
+ */
71
+ export declare function compileGeoJsonFormat(schemaObj: ValidationObject, jsonSchema: JSONSchema): (data: unknown, dataPath?: string) => boolean;
72
+ /**
73
+ * Object mapping geospatial format names to their compiler functions.
74
+ *
75
+ * @type {Record<string, (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean>}
76
+ */
77
+ export declare const formatValidators: Record<string, (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean>;
@@ -19,4 +19,5 @@ export { formatValidators as dateTimeFormats } from './datetime.js';
19
19
  export { formatValidators as stringFormats } from './string.js';
20
20
  export { formatValidators as numberFormats } from './number.js';
21
21
  export { formatValidators as jsonFormats } from './json.js';
22
- export { formatTesters, stringFormatTesters, jsonFormatTesters, dateTimeFormatTesters, numberFormatTesters, } from './testers.js';
22
+ export { formatValidators as geoFormats } from './geo.js';
23
+ export { formatTesters, stringFormatTesters, jsonFormatTesters, geoFormatTesters, dateTimeFormatTesters, numberFormatTesters, } from './testers.js';
@@ -63,6 +63,30 @@ export declare const compileRelativeJsonPointerFormat: (schemaObj: import("./str
63
63
  * compileJsonPathFormat(schemaObj, { format: 'json-path' })('$.foo '); // false (trailing whitespace)
64
64
  */
65
65
  export declare const compileJsonPathFormat: (schemaObj: import("./string.js").ValidationObject, jsonSchema: import("./string.js").JSONSchema) => (data: unknown, dataPath?: string) => boolean;
66
+ /**
67
+ * Compiles a validator for the 'json-path-segments' format.
68
+ * Validates a *variable-rooted* path string: `$name` followed by
69
+ * optional RFC 9535 segments (`$book.price[?@.isbn]`). Such a string is
70
+ * not a valid RFC 9535 query — the RFC's root identifier is `$` alone —
71
+ * so `json-path` would reject it; this format is what gives the Jaren
72
+ * query format's variable-rooted paths the same schema-time
73
+ * well-formedness that absolute paths get from `json-path`.
74
+ *
75
+ * Both formats recognize the five built-in function extensions and no
76
+ * others: a format is a property of the string itself, so it must mean
77
+ * the same thing in every schema regardless of which custom extensions
78
+ * a particular host registered.
79
+ *
80
+ * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
81
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
82
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
83
+ * @example
84
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book.price'); // true
85
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book'); // true
86
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$.price'); // false (that is json-path)
87
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book.price['); // false
88
+ */
89
+ export declare const compileJsonPathSegmentsFormat: (schemaObj: import("./string.js").ValidationObject, jsonSchema: import("./string.js").JSONSchema) => (data: unknown, dataPath?: string) => boolean;
66
90
  /**
67
91
  * Object mapping JSON-related format names to their compiler functions.
68
92
  *
@@ -135,25 +135,28 @@ export declare const compileColorFormat: (schemaObj: ValidationObject, jsonSchem
135
135
  */
136
136
  export declare const compileRegexFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
137
137
  /**
138
- * Compiles a validator for the 'uri' format.
139
- * Validates absolute URI strings per RFC 3986.
138
+ * Compiles a validator for the 'iregexp' format.
139
+ * Validates that a string is a valid I-Regexp (RFC 9485) pattern - the
140
+ * interoperable subset that carries the same meaning across regexp
141
+ * dialects. Stricter than 'regex': shorthand classes (\d, \w), lazy
142
+ * quantifiers, anchors and lookaround are all rejected.
140
143
  *
141
144
  * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
142
145
  * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
143
146
  * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
144
- * @example
145
- * compileUriFormat(schemaObj, { format: 'uri' })('https://example.com'); // true
146
147
  */
147
- export declare const compileUriFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
148
+ export declare const compileIRegexpFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
148
149
  /**
149
- * Compiles a validator for the 'uri--full' format.
150
- * Validates absolute URI strings with stricter checking.
150
+ * Compiles a validator for the 'uri' format.
151
+ * Validates absolute URI strings per RFC 3986.
151
152
  *
152
153
  * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
153
154
  * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
154
155
  * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
156
+ * @example
157
+ * compileUriFormat(schemaObj, { format: 'uri' })('https://example.com'); // true
155
158
  */
156
- export declare const compileUriFullFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
159
+ export declare const compileUriFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
157
160
  /**
158
161
  * Compiles a validator for the 'uri-reference' format.
159
162
  * Validates URI reference strings (absolute or relative) per RFC 3986.
@@ -163,15 +166,6 @@ export declare const compileUriFullFormat: (schemaObj: ValidationObject, jsonSch
163
166
  * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
164
167
  */
165
168
  export declare const compileUriReferenceFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
166
- /**
167
- * Compiles a validator for the 'uri-reference--full' format.
168
- * Validates URI reference strings with stricter checking.
169
- *
170
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
171
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
172
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
173
- */
174
- export declare const compileUriReferenceFullFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
175
169
  /**
176
170
  * Compiles a validator for the 'uri-template' format.
177
171
  * Validates URI template strings per RFC 6570.
@@ -190,15 +184,6 @@ export declare const compileUriTemplateFormat: (schemaObj: ValidationObject, jso
190
184
  * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
191
185
  */
192
186
  export declare const compileUrlFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
193
- /**
194
- * Compiles a validator for the 'url--full' format.
195
- * Validates URL strings with stricter checking.
196
- *
197
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
198
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
199
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
200
- */
201
- export declare const compileUrlFullFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
202
187
  /**
203
188
  * Compiles a validator for the 'iri' format.
204
189
  * Validates IRI strings per RFC 3987.
@@ -228,15 +213,6 @@ export declare const compileIriReferenceFormat: (schemaObj: ValidationObject, js
228
213
  * compileEmailFormat(schemaObj, { format: 'email' })('user@example.com'); // true
229
214
  */
230
215
  export declare const compileEmailFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
231
- /**
232
- * Compiles a validator for the 'email--full' format.
233
- * Validates email address strings with stricter checking.
234
- *
235
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
236
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
237
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
238
- */
239
- export declare const compileEmailFullFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
240
216
  /**
241
217
  * Compiles a validator for the 'idn-email' format.
242
218
  * Validates internationalized email addresses (EAI) per RFC 6531.
@@ -18,6 +18,14 @@ export declare const jsonFormatTesters: Record<string, StringFormatTester>;
18
18
  * @type {Record<string, StringFormatTester>}
19
19
  */
20
20
  export declare const dateTimeFormatTesters: Record<string, StringFormatTester>;
21
+ /**
22
+ * Geospatial format testers (the geo.js compiler group). `geohash` and
23
+ * `wkt` take strings; `geojson` takes the OBJECT value — the quick
24
+ * yes-or-no twin of the GeoJSON meta-schema artifacts in `@jarenjs/json`,
25
+ * including the ring-closure invariant JSON Schema cannot express.
26
+ * @type {Record<string, (value: any) => boolean>}
27
+ */
28
+ export declare const geoFormatTesters: Record<string, (value: any) => boolean>;
21
29
  /**
22
30
  * Number format testers (the number.js compiler group). These take the
23
31
  * NUMBER value, not a string.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/formats",
3
3
  "private": false,
4
- "version": "0.9.2",
4
+ "version": "0.34.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "license": "MIT",
28
28
  "engines": {
29
- "node": ">=22"
29
+ "node": ">=24"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public",
@@ -43,7 +43,7 @@
43
43
  "prepack": "npm run build:types"
44
44
  },
45
45
  "dependencies": {
46
- "@jarenjs/core": "^0.9.2",
47
- "@jarenjs/json": "^0.9.2"
46
+ "@jarenjs/core": "^0.34.0",
47
+ "@jarenjs/json": "^0.34.0"
48
48
  }
49
49
  }
package/src/datetime.js CHANGED
@@ -7,14 +7,20 @@ import {
7
7
 
8
8
  import {
9
9
  isDateType,
10
+ isDateTimeRFC3339,
11
+ isDateOnlyRFC3339,
12
+ isTimeOnlyRFC3339,
10
13
  getDateTypeOfDateTimeRFC3339,
11
14
  getDateTypeOfDateOnlyRFC3339,
12
15
  getDateTypeOfTimeOnlyRFC3339,
16
+ getEpochOfDateTimeRFC3339,
17
+ getEpochOfDateOnlyRFC3339,
18
+ getEpochOfTimeOnlyRFC3339,
13
19
  isValidDuration,
14
- isValidISODateTime,
15
- isValidISOTime,
16
20
  getDateTypeOfISODateTime,
17
21
  getDateTypeOfISOTime,
22
+ getEpochOfISODateTime,
23
+ getEpochOfISOTime,
18
24
  } from '@jarenjs/core/dates';
19
25
 
20
26
  /**
@@ -25,6 +31,16 @@ import {
25
31
  * }} ValidationObject
26
32
  */
27
33
 
34
+ // The bound validators compare epoch milliseconds: the bound is folded
35
+ // to a number at compile time and the value arrives as either an epoch
36
+ // number (the string path, which then allocates nothing) or a raw Date
37
+ // instance (which the relational operators coerce numerically, keeping
38
+ // that door open). Only the error path materializes a Date, so the
39
+ // reported error value stays what it always was.
40
+ function asDateValue(value) {
41
+ return typeof value === 'number' ? new Date(value) : value;
42
+ }
43
+
28
44
  /**
29
45
  * Compiles a format minimum validator function for date/time types.
30
46
  * Supports both inclusive (formatMinimum) and exclusive (formatExclusiveMinimum) bounds.
@@ -32,7 +48,7 @@ import {
32
48
  * @param {(value: string) => Date | undefined} parseType - Function to parse string into Date
33
49
  * @param {ValidationObject} schemaObj - The validation object for error handling and options
34
50
  * @param {JSONSchema} jsonSchema - The JSON schema containing format constraints
35
- * @returns {((date: Date, dataPath?: string) => boolean) | undefined} A validator function or undefined if no minimum constraint
51
+ * @returns {((date: number | Date, dataPath?: string) => boolean) | undefined} A validator function or undefined if no minimum constraint
36
52
  */
37
53
  function compileFormatMinimumByType(parseType, schemaObj, jsonSchema) {
38
54
  const [min, emin] = getInclusiveExclusiveBounds(
@@ -43,18 +59,20 @@ function compileFormatMinimumByType(parseType, schemaObj, jsonSchema) {
43
59
 
44
60
  if (emin != null) {
45
61
  const addError = schemaObj.createErrorHandler(emin, 'formatExclusiveMinimum');
62
+ const bound = emin.valueOf();
46
63
 
47
64
  return function isFormatExclusiveMinimum(date, dataPath) {
48
- return date > emin
49
- || addError(date, dataPath);
65
+ return date > bound
66
+ || addError(asDateValue(date), dataPath);
50
67
  };
51
68
  }
52
69
  else if (min) {
53
70
  const addError = schemaObj.createErrorHandler(min, 'formatMinimum');
71
+ const bound = min.valueOf();
54
72
 
55
73
  return function isFormatMinimum(date, dataPath) {
56
- return date >= min
57
- || addError(date, dataPath);
74
+ return date >= bound
75
+ || addError(asDateValue(date), dataPath);
58
76
  };
59
77
  }
60
78
 
@@ -68,7 +86,7 @@ function compileFormatMinimumByType(parseType, schemaObj, jsonSchema) {
68
86
  * @param {(value: string) => Date | undefined} parseType - Function to parse string into Date
69
87
  * @param {ValidationObject} schemaObj - The validation object for error handling and options
70
88
  * @param {JSONSchema} jsonSchema - The JSON schema containing format constraints
71
- * @returns {((date: Date, dataPath?: string) => boolean) | undefined} A validator function or undefined if no maximum constraint
89
+ * @returns {((date: number | Date, dataPath?: string) => boolean) | undefined} A validator function or undefined if no maximum constraint
72
90
  */
73
91
  function compileFormatMaximumByType(parseType, schemaObj, jsonSchema) {
74
92
  const [max, emax] = getInclusiveExclusiveBounds(
@@ -79,18 +97,20 @@ function compileFormatMaximumByType(parseType, schemaObj, jsonSchema) {
79
97
 
80
98
  if (emax != null) {
81
99
  const addError = schemaObj.createErrorHandler(emax, 'formatExclusiveMaximum');
100
+ const bound = emax.valueOf();
82
101
 
83
102
  return function isFormatExclusiveMaximum(date, dataPath) {
84
- return date < emax
85
- || addError(date, dataPath);
103
+ return date < bound
104
+ || addError(asDateValue(date), dataPath);
86
105
  };
87
106
  }
88
107
  else if (max) {
89
108
  const addError = schemaObj.createErrorHandler(max, 'formatMaximum');
109
+ const bound = max.valueOf();
90
110
 
91
111
  return function isFormatMaximum(date, dataPath) {
92
- return date <= max
93
- || addError(date, dataPath);
112
+ return date <= bound
113
+ || addError(asDateValue(date), dataPath);
94
114
  };
95
115
  }
96
116
 
@@ -105,6 +125,11 @@ function compileFormatMaximumByType(parseType, schemaObj, jsonSchema) {
105
125
  * @param {(value: string) => Date | undefined} parseType - Function to parse string into Date
106
126
  * @param {ValidationObject} schemaObj - The validation object for error handling and options
107
127
  * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
128
+ * @param {(value: string) => boolean} [isType] - Boolean tester matching parseType's
129
+ * accepted grammar; used on the boundless path so no Date is constructed
130
+ * @param {(value: string) => number | undefined} [parseEpoch] - Epoch twin of
131
+ * parseType; used on the bounded path so string values compare as numbers
132
+ * without constructing a Date
108
133
  * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
109
134
  * @example
110
135
  * // Basic format validation
@@ -116,7 +141,7 @@ function compileFormatMaximumByType(parseType, schemaObj, jsonSchema) {
116
141
  * formatMinimum: '2024-01-01T00:00:00Z'
117
142
  * })('2024-06-15T12:00:00Z'); // true
118
143
  */
119
- function compileFormatByType(name, parseType, schemaObj, jsonSchema) {
144
+ function compileFormatByType(name, parseType, schemaObj, jsonSchema, isType = undefined, parseEpoch = undefined) {
120
145
  if (jsonSchema.format !== name)
121
146
  throw new Error('ERROR: This should not happen!');
122
147
 
@@ -134,10 +159,14 @@ function compileFormatByType(name, parseType, schemaObj, jsonSchema) {
134
159
  jsonSchema,
135
160
  );
136
161
 
162
+ // the bounded paths parse string values with the epoch twin where one
163
+ // exists, so a passing validation allocates nothing
164
+ const parseValue = parseEpoch != null ? parseEpoch : parseType;
165
+
137
166
  if (validateMin != null && validateMax != null) {
138
167
  return function validateFormatBetween(data, dataPath) {
139
168
  if (isStringType(data)) {
140
- const date = parseType(data);
169
+ const date = parseValue(data);
141
170
  return date == null
142
171
  ? addError(data, dataPath)
143
172
  : validateMin(date, dataPath)
@@ -155,7 +184,7 @@ function compileFormatByType(name, parseType, schemaObj, jsonSchema) {
155
184
  if (validateMin != null) {
156
185
  return function validateFormatMinimum(data, dataPath) {
157
186
  if (isStringType(data)) {
158
- const date = parseType(data);
187
+ const date = parseValue(data);
159
188
  return date == null
160
189
  ? addError(data, dataPath)
161
190
  : validateMin(date, dataPath);
@@ -170,10 +199,10 @@ function compileFormatByType(name, parseType, schemaObj, jsonSchema) {
170
199
  if (validateMax != null) {
171
200
  return function validateFormatMaximum(data, dataPath) {
172
201
  if (isStringType(data)) {
173
- const date = parseType(data);
202
+ const date = parseValue(data);
174
203
  return date == null
175
204
  ? addError(data, dataPath)
176
- : validateMax(date);
205
+ : validateMax(date, dataPath);
177
206
  }
178
207
  else if (isDateType(data))
179
208
  // @ts-ignore
@@ -183,6 +212,16 @@ function compileFormatByType(name, parseType, schemaObj, jsonSchema) {
183
212
  };
184
213
  }
185
214
 
215
+ // Without bounds only the format assertion remains; the boolean tester
216
+ // avoids parsing the string into a Date that would be discarded.
217
+ if (isType != null) {
218
+ return function validateDateTimeFormatOnly(data, dataPath) {
219
+ return isStringType(data)
220
+ ? isType(data) || addError(data, dataPath)
221
+ : true;
222
+ };
223
+ }
224
+
186
225
  return function validateDateTime(data, dataPath) {
187
226
  if (isStringType(data)) {
188
227
  const date = parseType(data);
@@ -218,6 +257,8 @@ export function compileDateTimeFormat(schemaObj, jsonSchema) {
218
257
  getDateTypeOfDateTimeRFC3339,
219
258
  schemaObj,
220
259
  jsonSchema,
260
+ isDateTimeRFC3339,
261
+ getEpochOfDateTimeRFC3339,
221
262
  );
222
263
  }
223
264
 
@@ -239,6 +280,8 @@ export function compileDateOnlyFormat(schemaObj, jsonSchema) {
239
280
  getDateTypeOfDateOnlyRFC3339,
240
281
  schemaObj,
241
282
  jsonSchema,
283
+ isDateOnlyRFC3339,
284
+ getEpochOfDateOnlyRFC3339,
242
285
  );
243
286
  }
244
287
 
@@ -261,6 +304,8 @@ export function compileTimeOnlyFormat(schemaObj, jsonSchema) {
261
304
  getDateTypeOfTimeOnlyRFC3339,
262
305
  schemaObj,
263
306
  jsonSchema,
307
+ isTimeOnlyRFC3339,
308
+ getEpochOfTimeOnlyRFC3339,
264
309
  );
265
310
  }
266
311
 
@@ -289,7 +334,7 @@ export function compileDurationFormat(schemaObj, jsonSchema) {
289
334
 
290
335
  // when skipErrors is true, we don't need to create error objects
291
336
  if (schemaObj.options.skipErrors) {
292
- return function validateDurationFast(data, dataPath) {
337
+ return function validateDurationFast(data, _dataPath) {
293
338
  return isStringType(data)
294
339
  ? isValidDuration(data)
295
340
  : true;
@@ -330,6 +375,8 @@ export function compileISODateTimeFormat(schemaObj, jsonSchema) {
330
375
  getDateTypeOfISODateTime,
331
376
  schemaObj,
332
377
  jsonSchema,
378
+ undefined,
379
+ getEpochOfISODateTime,
333
380
  );
334
381
  }
335
382
 
@@ -354,6 +401,8 @@ export function compileISOTimeFormat(schemaObj, jsonSchema) {
354
401
  getDateTypeOfISOTime,
355
402
  schemaObj,
356
403
  jsonSchema,
404
+ undefined,
405
+ getEpochOfISOTime,
357
406
  );
358
407
  }
359
408
 
package/src/geo.js ADDED
@@ -0,0 +1,109 @@
1
+ //@ts-check
2
+
3
+ // The name -> predicate bindings live in ONE place: testers.js. This
4
+ // module only wraps them in the validator's compiler contract.
5
+ import { geoFormatTesters } from './testers.js';
6
+
7
+ import { createStringFormatCompiler } from './string.js';
8
+
9
+ /**
10
+ * @typedef {{format?: string, formatMinimum?: string, formatExclusiveMinimum?: string, formatMaximum?: string, formatExclusiveMaximum?: string}} JSONSchema
11
+ * @typedef {{
12
+ * options: {skipErrors: boolean},
13
+ * createErrorHandler: (expected: any, key: string, ...details: any[]) => (data: any, dataPath?: string) => boolean
14
+ * }} ValidationObject
15
+ */
16
+
17
+ // =============================================================================
18
+ // Geospatial Format Compilers
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Compiles a validator for the 'geohash' format.
23
+ * Validates geohash strings: any length, every character in the
24
+ * base-32 geohash alphabet.
25
+ *
26
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
27
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
28
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
29
+ * @example
30
+ * compileGeohashFormat(schemaObj, { format: 'geohash' })('u173z'); // true
31
+ * compileGeohashFormat(schemaObj, { format: 'geohash' })('u17a'); // false ('a' is not in the alphabet)
32
+ */
33
+ export const compileGeohashFormat = createStringFormatCompiler('geohash', geoFormatTesters['geohash']);
34
+
35
+ /**
36
+ * Compiles a validator for the 'wkt' format.
37
+ * Validates Well-Known Text geometry strings (ISO 19125 / OGC Simple
38
+ * Features): the seven tagged geometry types with optional Z/M/ZM
39
+ * modifiers, consistent coordinate counts, and closed polygon rings.
40
+ *
41
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
42
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
43
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
44
+ * @example
45
+ * compileWktFormat(schemaObj, { format: 'wkt' })('POINT (4.9041 52.3676)'); // true
46
+ * compileWktFormat(schemaObj, { format: 'wkt' })('POLYGON ((0 0, 4 0, 4 4, 1 1))'); // false (open ring)
47
+ */
48
+ export const compileWktFormat = createStringFormatCompiler('wkt', geoFormatTesters['wkt']);
49
+
50
+ /**
51
+ * Compiles a validator for the 'geojson' format. Unlike the string
52
+ * formats, this one applies to **objects**: any non-array object must be
53
+ * a structurally valid GeoJSON object (RFC 7946) — the coordinate
54
+ * nesting its `type` requires, positions inside the WGS 84 bounds, and
55
+ * every linear ring closed. Non-object values pass, following the rule
56
+ * that a format constrains only its own type.
57
+ *
58
+ * This is the quick, shallow judgment; the GeoJSON meta-schema artifacts
59
+ * in `@jarenjs/json` validate the same grammar more thoroughly (locating
60
+ * the failure, and — in the Jaren-extended variant — checking ring
61
+ * winding through `$query`). Reach for the schema when you want to know
62
+ * *what* is wrong; reach for the format when a one-keyword annotation is
63
+ * worth more than a diagnosis.
64
+ *
65
+ * @param {ValidationObject} schemaObj - The validation object for error handling and options
66
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
67
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
68
+ * @example
69
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })({ type: 'Point', coordinates: [4.9, 52.4] }); // true
70
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })({ type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[2,2]]] }); // false (open ring)
71
+ * compileGeoJsonFormat(schemaObj, { format: 'geojson' })('not an object'); // true (wrong type is not this format's business)
72
+ */
73
+ export function compileGeoJsonFormat(schemaObj, jsonSchema) {
74
+ if (jsonSchema.format !== 'geojson')
75
+ throw new Error('Format is not equal to jsonSchema (should not happen!)');
76
+
77
+ const isGeoJson = geoFormatTesters['geojson'];
78
+
79
+ if (schemaObj.options.skipErrors) {
80
+ return function validateGeoJsonFormatFast(data, _dataPath) {
81
+ return data === null || typeof data !== 'object' || Array.isArray(data)
82
+ ? true
83
+ : isGeoJson(data);
84
+ };
85
+ }
86
+
87
+ const addError = schemaObj.createErrorHandler('geojson', 'format', isGeoJson.constructor.name);
88
+
89
+ return function validateGeoJsonFormat(data, dataPath) {
90
+ return data === null || typeof data !== 'object' || Array.isArray(data)
91
+ ? true
92
+ : isGeoJson(data) || addError(data, dataPath);
93
+ };
94
+ }
95
+
96
+ // =============================================================================
97
+ // Aggregated Format Validators Object
98
+ // =============================================================================
99
+
100
+ /**
101
+ * Object mapping geospatial format names to their compiler functions.
102
+ *
103
+ * @type {Record<string, (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean>}
104
+ */
105
+ export const formatValidators = {
106
+ 'geohash': compileGeohashFormat,
107
+ 'wkt': compileWktFormat,
108
+ 'geojson': compileGeoJsonFormat,
109
+ };
package/src/index.js CHANGED
@@ -21,11 +21,13 @@ export { formatValidators as dateTimeFormats } from './datetime.js';
21
21
  export { formatValidators as stringFormats } from './string.js';
22
22
  export { formatValidators as numberFormats } from './number.js';
23
23
  export { formatValidators as jsonFormats } from './json.js';
24
+ export { formatValidators as geoFormats } from './geo.js';
24
25
 
25
26
  export {
26
27
  formatTesters,
27
28
  stringFormatTesters,
28
29
  jsonFormatTesters,
30
+ geoFormatTesters,
29
31
  dateTimeFormatTesters,
30
32
  numberFormatTesters,
31
33
  } from './testers.js';
package/src/json.js CHANGED
@@ -71,6 +71,31 @@ export const compileRelativeJsonPointerFormat = createStringFormatCompiler('rela
71
71
  */
72
72
  export const compileJsonPathFormat = createStringFormatCompiler('json-path', jsonFormatTesters['json-path']);
73
73
 
74
+ /**
75
+ * Compiles a validator for the 'json-path-segments' format.
76
+ * Validates a *variable-rooted* path string: `$name` followed by
77
+ * optional RFC 9535 segments (`$book.price[?@.isbn]`). Such a string is
78
+ * not a valid RFC 9535 query — the RFC's root identifier is `$` alone —
79
+ * so `json-path` would reject it; this format is what gives the Jaren
80
+ * query format's variable-rooted paths the same schema-time
81
+ * well-formedness that absolute paths get from `json-path`.
82
+ *
83
+ * Both formats recognize the five built-in function extensions and no
84
+ * others: a format is a property of the string itself, so it must mean
85
+ * the same thing in every schema regardless of which custom extensions
86
+ * a particular host registered.
87
+ *
88
+ * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
89
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
90
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
91
+ * @example
92
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book.price'); // true
93
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book'); // true
94
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$.price'); // false (that is json-path)
95
+ * compileJsonPathSegmentsFormat(schemaObj, { format: 'json-path-segments' })('$book.price['); // false
96
+ */
97
+ export const compileJsonPathSegmentsFormat = createStringFormatCompiler('json-path-segments', jsonFormatTesters['json-path-segments']);
98
+
74
99
  // =============================================================================
75
100
  // Aggregated Format Validators Object
76
101
  // =============================================================================
@@ -87,4 +112,5 @@ export const formatValidators = {
87
112
  'relative-json-pointer': compileRelativeJsonPointerFormat,
88
113
  // JSONPath
89
114
  'json-path': compileJsonPathFormat,
115
+ 'json-path-segments': compileJsonPathSegmentsFormat,
90
116
  };
package/src/string.js CHANGED
@@ -1,5 +1,4 @@
1
1
  //@ts-check
2
- // eslint-disable no-useless-escape
3
2
 
4
3
  import {
5
4
  isStringType,
@@ -35,7 +34,7 @@ export function createStringFormatCompiler(formatName, isFormatTest) {
35
34
 
36
35
  // when skipErrors is true, we don't need to create error objects
37
36
  if (schemaObj.options.skipErrors) {
38
- return function validateStringFormatFast(data, dataPath) {
37
+ return function validateStringFormatFast(data, _dataPath) {
39
38
  return isStringType(data)
40
39
  ? isFormatTest(data)
41
40
  : true;
@@ -183,6 +182,19 @@ export const compileColorFormat = createStringFormatCompiler('color', stringForm
183
182
  */
184
183
  export const compileRegexFormat = createStringFormatCompiler('regex', stringFormatTesters['regex']);
185
184
 
185
+ /**
186
+ * Compiles a validator for the 'iregexp' format.
187
+ * Validates that a string is a valid I-Regexp (RFC 9485) pattern - the
188
+ * interoperable subset that carries the same meaning across regexp
189
+ * dialects. Stricter than 'regex': shorthand classes (\d, \w), lazy
190
+ * quantifiers, anchors and lookaround are all rejected.
191
+ *
192
+ * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
193
+ * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
194
+ * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
195
+ */
196
+ export const compileIRegexpFormat = createStringFormatCompiler('iregexp', stringFormatTesters['iregexp']);
197
+
186
198
  // =============================================================================
187
199
  // URI Format Compilers
188
200
  // =============================================================================
@@ -199,16 +211,6 @@ export const compileRegexFormat = createStringFormatCompiler('regex', stringForm
199
211
  */
200
212
  export const compileUriFormat = createStringFormatCompiler('uri', stringFormatTesters['uri']);
201
213
 
202
- /**
203
- * Compiles a validator for the 'uri--full' format.
204
- * Validates absolute URI strings with stricter checking.
205
- *
206
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
207
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
208
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
209
- */
210
- export const compileUriFullFormat = createStringFormatCompiler('uri--full', stringFormatTesters['uri--full']);
211
-
212
214
  /**
213
215
  * Compiles a validator for the 'uri-reference' format.
214
216
  * Validates URI reference strings (absolute or relative) per RFC 3986.
@@ -219,16 +221,6 @@ export const compileUriFullFormat = createStringFormatCompiler('uri--full', stri
219
221
  */
220
222
  export const compileUriReferenceFormat = createStringFormatCompiler('uri-reference', stringFormatTesters['uri-reference']);
221
223
 
222
- /**
223
- * Compiles a validator for the 'uri-reference--full' format.
224
- * Validates URI reference strings with stricter checking.
225
- *
226
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
227
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
228
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
229
- */
230
- export const compileUriReferenceFullFormat = createStringFormatCompiler('uri-reference--full', stringFormatTesters['uri-reference--full']);
231
-
232
224
  /**
233
225
  * Compiles a validator for the 'uri-template' format.
234
226
  * Validates URI template strings per RFC 6570.
@@ -249,16 +241,6 @@ export const compileUriTemplateFormat = createStringFormatCompiler('uri-template
249
241
  */
250
242
  export const compileUrlFormat = createStringFormatCompiler('url', stringFormatTesters['url']);
251
243
 
252
- /**
253
- * Compiles a validator for the 'url--full' format.
254
- * Validates URL strings with stricter checking.
255
- *
256
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
257
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
258
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
259
- */
260
- export const compileUrlFullFormat = createStringFormatCompiler('url--full', stringFormatTesters['url--full']);
261
-
262
244
  // =============================================================================
263
245
  // IRI Format Compilers (Internationalized Resource Identifiers)
264
246
  // =============================================================================
@@ -299,16 +281,6 @@ export const compileIriReferenceFormat = createStringFormatCompiler('iri-referen
299
281
  */
300
282
  export const compileEmailFormat = createStringFormatCompiler('email', stringFormatTesters['email']);
301
283
 
302
- /**
303
- * Compiles a validator for the 'email--full' format.
304
- * Validates email address strings with stricter checking.
305
- *
306
- * @param {ValidationObject} schemaObj - The validation JSONSchema for error handling and options
307
- * @param {JSONSchema} jsonSchema - The JSON schema containing the format definition
308
- * @returns {(data: unknown, dataPath?: string) => boolean} A validator function
309
- */
310
- export const compileEmailFullFormat = createStringFormatCompiler('email--full', stringFormatTesters['email--full']);
311
-
312
284
  /**
313
285
  * Compiles a validator for the 'idn-email' format.
314
286
  * Validates internationalized email addresses (EAI) per RFC 6531.
@@ -510,20 +482,17 @@ export const formatValidators = {
510
482
  'color': compileColorFormat,
511
483
  // Regex
512
484
  'regex': compileRegexFormat,
485
+ 'iregexp': compileIRegexpFormat,
513
486
  // URI
514
487
  'uri': compileUriFormat,
515
- 'uri--full': compileUriFullFormat,
516
488
  'uri-reference': compileUriReferenceFormat,
517
- 'uri-reference--full': compileUriReferenceFullFormat,
518
489
  'uri-template': compileUriTemplateFormat,
519
490
  'url': compileUrlFormat,
520
- 'url--full': compileUrlFullFormat,
521
491
  // IRI
522
492
  'iri': compileIriFormat,
523
493
  'iri-reference': compileIriReferenceFormat,
524
494
  // Email
525
495
  'email': compileEmailFormat,
526
- 'email--full': compileEmailFullFormat,
527
496
  'idn-email': compileIdnEmailFormat,
528
497
  // Hostname
529
498
  'hostname': compileHostnameFormat,
package/src/testers.js CHANGED
@@ -23,14 +23,10 @@
23
23
 
24
24
  import {
25
25
  isValidUri,
26
- isValidUriFull,
27
26
  isValidUriRef,
28
- isValidUriRefFull,
29
27
  isValidUriTemplate,
30
28
  isValidUrl,
31
- isValidUrlFull,
32
29
  isValidEmail,
33
- isValidEmailFull,
34
30
  isValidIdnEmail,
35
31
  isValidHostname,
36
32
  isValidIdnHostname,
@@ -54,6 +50,7 @@ import {
54
50
  isValidBase64,
55
51
  isValidCountryAlpha2,
56
52
  isValidIBAN,
53
+ isValidIRegexp,
57
54
  } from '@jarenjs/core/text';
58
55
 
59
56
  import {
@@ -93,8 +90,15 @@ import {
93
90
  isValidJSONPointerUriFragment,
94
91
  isValidRelativeJSONPointer,
95
92
  isValidJSONPathStrict,
93
+ isValidJSONPathSegments,
96
94
  } from '@jarenjs/json';
97
95
 
96
+ import {
97
+ isValidGeohash,
98
+ isValidWkt,
99
+ isValidGeoJson,
100
+ } from '@jarenjs/core/geo';
101
+
98
102
  /** @typedef {(value: string) => boolean} StringFormatTester */
99
103
  /** @typedef {(value: number) => boolean} NumberFormatTester */
100
104
 
@@ -118,20 +122,17 @@ export const stringFormatTesters = {
118
122
  'color': isValidHexColor,
119
123
  // Regex
120
124
  'regex': isStringRegExp,
125
+ 'iregexp': isValidIRegexp,
121
126
  // URI
122
127
  'uri': isValidUri,
123
- 'uri--full': isValidUriFull,
124
128
  'uri-reference': isValidUriRef,
125
- 'uri-reference--full': isValidUriRefFull,
126
129
  'uri-template': isValidUriTemplate,
127
130
  'url': isValidUrl,
128
- 'url--full': isValidUrlFull,
129
131
  // IRI
130
132
  'iri': isValidIRI,
131
133
  'iri-reference': isValidIRIRef,
132
134
  // Email
133
135
  'email': isValidEmail,
134
- 'email--full': isValidEmailFull,
135
136
  'idn-email': isValidIdnEmail,
136
137
  // Hostname
137
138
  'hostname': isValidHostname,
@@ -164,6 +165,7 @@ export const jsonFormatTesters = {
164
165
  'json-pointer-uri-fragment': isValidJSONPointerUriFragment,
165
166
  'relative-json-pointer': isValidRelativeJSONPointer,
166
167
  'json-path': isValidJSONPathStrict,
168
+ 'json-path-segments': isValidJSONPathSegments,
167
169
  };
168
170
 
169
171
  /**
@@ -180,6 +182,19 @@ export const dateTimeFormatTesters = {
180
182
  'iso-time': isValidISOTime,
181
183
  };
182
184
 
185
+ /**
186
+ * Geospatial format testers (the geo.js compiler group). `geohash` and
187
+ * `wkt` take strings; `geojson` takes the OBJECT value — the quick
188
+ * yes-or-no twin of the GeoJSON meta-schema artifacts in `@jarenjs/json`,
189
+ * including the ring-closure invariant JSON Schema cannot express.
190
+ * @type {Record<string, (value: any) => boolean>}
191
+ */
192
+ export const geoFormatTesters = {
193
+ 'geohash': isValidGeohash,
194
+ 'wkt': isValidWkt,
195
+ 'geojson': isValidGeoJson,
196
+ };
197
+
183
198
  /**
184
199
  * Number format testers (the number.js compiler group). These take the
185
200
  * NUMBER value, not a string.
@@ -211,6 +226,7 @@ export const numberFormatTesters = {
211
226
  export const formatTesters = {
212
227
  ...stringFormatTesters,
213
228
  ...jsonFormatTesters,
229
+ ...geoFormatTesters,
214
230
  ...dateTimeFormatTesters,
215
231
  ...numberFormatTesters,
216
232
  };