@jarenjs/formats 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.
- package/README.md +34 -19
- package/dist/types/geo.d.ts +77 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/json.d.ts +24 -0
- package/dist/types/string.d.ts +11 -35
- package/dist/types/testers.d.ts +8 -0
- package/package.json +4 -4
- package/src/datetime.js +67 -18
- package/src/geo.js +109 -0
- package/src/index.js +2 -0
- package/src/json.js +26 -0
- package/src/string.js +15 -46
- package/src/testers.js +24 -8
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
|
|
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` |
|
|
48
|
-
- `
|
|
49
|
-
- `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
|
|
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` |
|
|
102
|
-
- `iban` | International Bank Account Number
|
|
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>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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 {
|
|
22
|
+
export { formatValidators as geoFormats } from './geo.js';
|
|
23
|
+
export { formatTesters, stringFormatTesters, jsonFormatTesters, geoFormatTesters, dateTimeFormatTesters, numberFormatTesters, } from './testers.js';
|
package/dist/types/json.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/types/string.d.ts
CHANGED
|
@@ -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 '
|
|
139
|
-
* Validates
|
|
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
|
|
148
|
+
export declare const compileIRegexpFormat: (schemaObj: ValidationObject, jsonSchema: JSONSchema) => (data: unknown, dataPath?: string) => boolean;
|
|
148
149
|
/**
|
|
149
|
-
* Compiles a validator for the 'uri
|
|
150
|
-
* Validates absolute URI strings
|
|
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
|
|
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.
|
package/dist/types/testers.d.ts
CHANGED
|
@@ -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.
|
|
4
|
+
"version": "0.34.2",
|
|
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": ">=
|
|
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.
|
|
47
|
-
"@jarenjs/json": "^0.
|
|
46
|
+
"@jarenjs/core": "^0.34.2",
|
|
47
|
+
"@jarenjs/json": "^0.34.2"
|
|
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 >
|
|
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 >=
|
|
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 <
|
|
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 <=
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
};
|