pema 0.11.0 → 0.12.1
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/lib/private/ArrayType.js +4 -4
- package/lib/private/BooleanType.d.ts +1 -1
- package/lib/private/DateType.d.ts +1 -0
- package/lib/private/DateType.js +3 -0
- package/lib/private/IssueType.d.ts +1 -1
- package/lib/private/JSONType.d.ts +5 -3
- package/lib/private/JSONType.js +31 -7
- package/lib/private/ObjectPolicy.d.ts +6 -0
- package/lib/private/ObjectPolicy.js +2 -0
- package/lib/private/ObjectType.d.ts +4 -1
- package/lib/private/ObjectType.js +34 -8
- package/lib/private/PartialType.d.ts +4 -1
- package/lib/private/PartialType.js +27 -3
- package/lib/private/PrimitiveType.js +2 -1
- package/lib/private/Serialized.d.ts +1 -0
- package/lib/private/StringType.js +5 -5
- package/lib/private/cardinality.d.ts +10 -0
- package/lib/private/cardinality.js +32 -0
- package/lib/private/coerce/date.js +4 -4
- package/lib/private/errors.d.ts +2 -0
- package/lib/private/errors.js +9 -0
- package/lib/private/index.d.ts +2 -0
- package/lib/private/index.js +2 -0
- package/lib/private/normalize.js +1 -1
- package/lib/private/parseable.d.ts +3 -0
- package/lib/private/parseable.js +3 -0
- package/lib/private/resolve.js +4 -3
- package/lib/private/schema-errors.d.ts +14 -11
- package/lib/private/schema-errors.js +7 -4
- package/lib/private/test.d.ts +1 -1
- package/lib/private/test.js +1 -0
- package/lib/private/validator/length.js +0 -7
- package/lib/private/validator/message.js +3 -0
- package/package.json +32 -32
package/lib/private/ArrayType.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cardinality from "#cardinality";
|
|
1
2
|
import DefaultType from "#DefaultType";
|
|
2
3
|
import EnumType from "#EnumType";
|
|
3
4
|
import E from "#errors";
|
|
@@ -60,16 +61,15 @@ export default class ArrayType extends GenericType {
|
|
|
60
61
|
return this.with({ validators: [uniqueBy(select)] });
|
|
61
62
|
}
|
|
62
63
|
min(limit) {
|
|
63
|
-
|
|
64
|
-
throw S.min_negative(limit);
|
|
64
|
+
cardinality.min(limit);
|
|
65
65
|
return this.with({ validators: [min(limit)] });
|
|
66
66
|
}
|
|
67
67
|
max(limit) {
|
|
68
|
-
|
|
69
|
-
throw S.max_negative(limit);
|
|
68
|
+
cardinality.max(limit);
|
|
70
69
|
return this.with({ validators: [max(limit)] });
|
|
71
70
|
}
|
|
72
71
|
length(from, to) {
|
|
72
|
+
cardinality.length(from, to);
|
|
73
73
|
return this.with({ validators: [length(from, to)] });
|
|
74
74
|
}
|
|
75
75
|
parse(u, options = {}) {
|
|
@@ -8,7 +8,7 @@ export default class BooleanType<M extends Mode = undefined> extends PrimitiveTy
|
|
|
8
8
|
constructor(mode?: M);
|
|
9
9
|
get name(): "boolean";
|
|
10
10
|
get datatype(): "boolean";
|
|
11
|
-
[CoerceKey](x: unknown):
|
|
11
|
+
[CoerceKey](x: unknown): boolean;
|
|
12
12
|
toJSON(): {
|
|
13
13
|
type: "boolean";
|
|
14
14
|
datatype: "boolean";
|
|
@@ -10,6 +10,7 @@ export default class DateType<M extends boolean | undefined = undefined> extends
|
|
|
10
10
|
get Type(): DateConstructor;
|
|
11
11
|
get name(): "date";
|
|
12
12
|
get datatype(): "datetime";
|
|
13
|
+
now(): import("./DefaultType.js").default<this, Date>;
|
|
13
14
|
toJSON(): {
|
|
14
15
|
type: "date";
|
|
15
16
|
datatype: "datetime";
|
package/lib/private/DateType.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
type IssueType = "invalid_type" | "invalid_format" | "too_small" | "too_large" | "out_of_range" | "not_in_set" | "duplicate" | "network_error";
|
|
1
|
+
type IssueType = "invalid_type" | "invalid_format" | "too_small" | "too_large" | "out_of_range" | "not_in_set" | "duplicate" | "unknown_property" | "network_error";
|
|
2
2
|
export type { IssueType as default };
|
|
3
3
|
//# sourceMappingURL=IssueType.d.ts.map
|
|
@@ -8,12 +8,13 @@ import Storable from "#Storable";
|
|
|
8
8
|
import type { JSONValue } from "@rcompat/type";
|
|
9
9
|
export type ParsedJSON = Parsed<JSONValue>;
|
|
10
10
|
type JSONInput = ParsedJSON | undefined;
|
|
11
|
-
type JSONInfer<S extends JSONInput> = S extends ParsedJSON ? Infer<S> : JSONValue;
|
|
12
|
-
export default class JSONType<S extends JSONInput = undefined> extends Storable<"json", JSONInfer<S>> {
|
|
11
|
+
type JSONInfer<S extends JSONInput, D extends boolean> = D extends true ? Record<string, JSONValue> : S extends ParsedJSON ? Infer<S> : JSONValue;
|
|
12
|
+
export default class JSONType<S extends JSONInput = undefined, D extends boolean = false> extends Storable<"json", JSONInfer<S, D>> {
|
|
13
13
|
#private;
|
|
14
|
-
constructor(inner?: S);
|
|
14
|
+
constructor(inner?: S, dictionary?: D);
|
|
15
15
|
get name(): "json";
|
|
16
16
|
get datatype(): "json";
|
|
17
|
+
dict(this: JSONType<undefined, false>): JSONType<undefined, true>;
|
|
17
18
|
optional(): OptionalType<this>;
|
|
18
19
|
default(value: (() => Infer<this>) | Infer<this>): DefaultType<this, Infer<this>>;
|
|
19
20
|
parse(u: unknown, options?: ParseOptions): Infer<this>;
|
|
@@ -21,6 +22,7 @@ export default class JSONType<S extends JSONInput = undefined> extends Storable<
|
|
|
21
22
|
type: "json";
|
|
22
23
|
datatype: "json";
|
|
23
24
|
of?: Serialized;
|
|
25
|
+
dictionary?: true;
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
export {};
|
package/lib/private/JSONType.js
CHANGED
|
@@ -4,19 +4,36 @@ import OptionalType from "#OptionalType";
|
|
|
4
4
|
import resolve from "#resolve";
|
|
5
5
|
import Storable from "#Storable";
|
|
6
6
|
import is from "@rcompat/is";
|
|
7
|
-
function json_with_undefined_object_values(x) {
|
|
8
|
-
if (is.array(x))
|
|
9
|
-
return
|
|
10
|
-
if (
|
|
11
|
-
return
|
|
7
|
+
function json_with_undefined_object_values(x, ancestors = new Set()) {
|
|
8
|
+
if (!is.array(x) && !is.dict(x))
|
|
9
|
+
return is.json(x);
|
|
10
|
+
if (ancestors.has(x))
|
|
11
|
+
return false;
|
|
12
|
+
ancestors.add(x);
|
|
13
|
+
let valid = true;
|
|
14
|
+
if (is.array(x)) {
|
|
15
|
+
for (let index = 0; index < x.length; index++) {
|
|
16
|
+
if (!Object.hasOwn(x, index)
|
|
17
|
+
|| !json_with_undefined_object_values(x[index], ancestors)) {
|
|
18
|
+
valid = false;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
valid = Object.values(x).every(value => is.undefined(value)
|
|
25
|
+
|| json_with_undefined_object_values(value, ancestors));
|
|
12
26
|
}
|
|
13
|
-
|
|
27
|
+
ancestors.delete(x);
|
|
28
|
+
return valid;
|
|
14
29
|
}
|
|
15
30
|
export default class JSONType extends Storable {
|
|
16
31
|
#inner;
|
|
17
|
-
|
|
32
|
+
#dictionary;
|
|
33
|
+
constructor(inner, dictionary = false) {
|
|
18
34
|
super();
|
|
19
35
|
this.#inner = inner;
|
|
36
|
+
this.#dictionary = dictionary;
|
|
20
37
|
}
|
|
21
38
|
get name() {
|
|
22
39
|
return "json";
|
|
@@ -24,6 +41,9 @@ export default class JSONType extends Storable {
|
|
|
24
41
|
get datatype() {
|
|
25
42
|
return "json";
|
|
26
43
|
}
|
|
44
|
+
dict() {
|
|
45
|
+
return new JSONType(undefined, true);
|
|
46
|
+
}
|
|
27
47
|
optional() {
|
|
28
48
|
return new OptionalType(this);
|
|
29
49
|
}
|
|
@@ -32,6 +52,9 @@ export default class JSONType extends Storable {
|
|
|
32
52
|
}
|
|
33
53
|
parse(u, options = {}) {
|
|
34
54
|
const x = resolve(u);
|
|
55
|
+
if (this.#dictionary && !is.dict(x)) {
|
|
56
|
+
throw E.invalid_type(x, "dictionary", options);
|
|
57
|
+
}
|
|
35
58
|
// delegate to inner schema for typed validation
|
|
36
59
|
if (is.defined(this.#inner))
|
|
37
60
|
return this.#inner.parse(x, options);
|
|
@@ -45,6 +68,7 @@ export default class JSONType extends Storable {
|
|
|
45
68
|
type: "json",
|
|
46
69
|
datatype: "json",
|
|
47
70
|
...is.defined(this.#inner) && { of: this.#inner.toJSON() },
|
|
71
|
+
...this.#dictionary && { dictionary: true },
|
|
48
72
|
};
|
|
49
73
|
}
|
|
50
74
|
}
|
|
@@ -4,6 +4,7 @@ import type InferInputSchema from "#InferInputSchema";
|
|
|
4
4
|
import Loose from "#Loose";
|
|
5
5
|
import type Mode from "#Mode";
|
|
6
6
|
import OptionalType from "#OptionalType";
|
|
7
|
+
import type ObjectPolicy from "#ObjectPolicy";
|
|
7
8
|
import type Parsed from "#Parsed";
|
|
8
9
|
import type ParseOptions from "#ParseOptions";
|
|
9
10
|
import type Serialized from "#Serialized";
|
|
@@ -16,11 +17,13 @@ export default class ObjectType<P extends Dict<Parsed<unknown>> = Dict<Parsed<un
|
|
|
16
17
|
#private;
|
|
17
18
|
[Loose]: M;
|
|
18
19
|
readonly Complement: ObjectType<Record<Exclude<string, keyof P>, Parsed<unknown>>>;
|
|
19
|
-
constructor(properties: P, mode?: M, options?: ParseOptions);
|
|
20
|
+
constructor(properties: P, mode?: M, options?: ParseOptions, policy?: ObjectPolicy);
|
|
20
21
|
get name(): string;
|
|
21
22
|
get properties(): P;
|
|
22
23
|
get input(): InferInputSchema<P>;
|
|
23
24
|
optional(): OptionalType<this>;
|
|
25
|
+
closed(): ObjectType<P, M, I>;
|
|
26
|
+
min(limit: number): ObjectType<P, M, I>;
|
|
24
27
|
shape<T>(): ObjectType<P, M, T>;
|
|
25
28
|
extend<E extends {
|
|
26
29
|
[K in keyof E]: K extends keyof P ? never : Parsed<unknown>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import cardinality from "#cardinality";
|
|
1
2
|
import E from "#errors";
|
|
2
3
|
import GenericType from "#GenericType";
|
|
3
4
|
import Loose from "#Loose";
|
|
4
5
|
import OptionalType from "#OptionalType";
|
|
6
|
+
import ParsedKey from "#ParsedKey";
|
|
5
7
|
import next from "#path/next";
|
|
6
8
|
import resolve from "#resolve";
|
|
7
9
|
import SE from "#schema-errors";
|
|
@@ -9,12 +11,14 @@ import is from "@rcompat/is";
|
|
|
9
11
|
export default class ObjectType extends GenericType {
|
|
10
12
|
#properties;
|
|
11
13
|
#options;
|
|
14
|
+
#policy;
|
|
12
15
|
[Loose];
|
|
13
|
-
constructor(properties, mode, options = {}) {
|
|
16
|
+
constructor(properties, mode, options = {}, policy = {}) {
|
|
14
17
|
super();
|
|
15
18
|
this.#properties = properties;
|
|
16
19
|
this[Loose] = mode;
|
|
17
20
|
this.#options = options;
|
|
21
|
+
this.#policy = policy;
|
|
18
22
|
}
|
|
19
23
|
get name() {
|
|
20
24
|
const props = Object.entries(this.#properties)
|
|
@@ -30,17 +34,22 @@ export default class ObjectType extends GenericType {
|
|
|
30
34
|
}
|
|
31
35
|
#derive(options) {
|
|
32
36
|
const Constructor = this.constructor;
|
|
33
|
-
const instance = new Constructor(this.#properties, this[Loose], {
|
|
34
|
-
...this.#options, ...options,
|
|
35
|
-
});
|
|
37
|
+
const instance = new Constructor(this.#properties, this[Loose], { ...this.#options, ...options }, this.#policy);
|
|
36
38
|
instance[Loose] = this[Loose];
|
|
37
39
|
return instance;
|
|
38
40
|
}
|
|
39
41
|
optional() {
|
|
40
42
|
return new OptionalType(this);
|
|
41
43
|
}
|
|
44
|
+
closed() {
|
|
45
|
+
return new ObjectType(this.#properties, this[Loose], this.#options, { ...this.#policy, closed: true });
|
|
46
|
+
}
|
|
47
|
+
min(limit) {
|
|
48
|
+
cardinality.min(limit);
|
|
49
|
+
return new ObjectType(this.#properties, this[Loose], this.#options, { ...this.#policy, minimum: limit });
|
|
50
|
+
}
|
|
42
51
|
shape() {
|
|
43
|
-
return new ObjectType(this.#properties, this[Loose], this.#options);
|
|
52
|
+
return new ObjectType(this.#properties, this[Loose], this.#options, this.#policy);
|
|
44
53
|
}
|
|
45
54
|
extend(extra) {
|
|
46
55
|
const properties = extra instanceof ObjectType ? extra.properties : extra;
|
|
@@ -48,22 +57,39 @@ export default class ObjectType extends GenericType {
|
|
|
48
57
|
if (key in this.#properties)
|
|
49
58
|
throw SE.extend_key_collision(key);
|
|
50
59
|
}
|
|
51
|
-
return new ObjectType({ ...this.#properties, ...properties }, this[Loose], this.#options);
|
|
60
|
+
return new ObjectType({ ...this.#properties, ...properties }, this[Loose], this.#options, this.#policy);
|
|
52
61
|
}
|
|
53
62
|
parse(u, options = {}) {
|
|
54
63
|
const x = resolve(u);
|
|
55
64
|
const $options = is.defined(this[Loose])
|
|
56
65
|
? { ...this.#options, ...options, [Loose]: this[Loose] }
|
|
57
66
|
: { ...this.#options, ...options };
|
|
58
|
-
if (is.defined(x) && !is.dict(x))
|
|
67
|
+
if (is.defined(x) && !is.dict(x)) {
|
|
59
68
|
throw E.invalid_type(x, "object", $options);
|
|
69
|
+
}
|
|
60
70
|
const input = x ?? {};
|
|
61
71
|
const out = {};
|
|
62
|
-
|
|
72
|
+
if (this.#policy.closed === true) {
|
|
73
|
+
for (const key of Object.keys(input)) {
|
|
74
|
+
if (!Object.hasOwn(this.#properties, key)) {
|
|
75
|
+
throw E.unknown_property(input[key], next(key, $options));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
let count = 0;
|
|
80
|
+
for (const k of Object.keys(this.#properties)) {
|
|
63
81
|
const parsed = this.#properties[k].parse(input[k], next(k, $options));
|
|
82
|
+
if (Object.prototype.propertyIsEnumerable.call(input, k) &&
|
|
83
|
+
is.defined(input[k])) {
|
|
84
|
+
count++;
|
|
85
|
+
}
|
|
64
86
|
if (is.defined(parsed))
|
|
65
87
|
out[k] = parsed;
|
|
66
88
|
}
|
|
89
|
+
const minimum = this.#policy.minimum;
|
|
90
|
+
if (is.defined(minimum) && count < minimum) {
|
|
91
|
+
throw E.too_small(input, `min ${minimum} properties`, $options[ParsedKey] ?? "");
|
|
92
|
+
}
|
|
67
93
|
return out;
|
|
68
94
|
}
|
|
69
95
|
toJSON() {
|
|
@@ -2,6 +2,7 @@ import DefaultType from "#DefaultType";
|
|
|
2
2
|
import type Infer from "#Infer";
|
|
3
3
|
import Loose from "#Loose";
|
|
4
4
|
import type Mode from "#Mode";
|
|
5
|
+
import type ObjectPolicy from "#ObjectPolicy";
|
|
5
6
|
import type ParseOptions from "#ParseOptions";
|
|
6
7
|
import type Partialable from "#Partialable";
|
|
7
8
|
import type DefaultTrait from "#trait/Default";
|
|
@@ -12,10 +13,12 @@ type InferPartial<D extends Partialable> = {
|
|
|
12
13
|
export default class PartialType<D extends Partialable, M extends Mode = undefined> extends VirtualType<D, InferPartial<D>, "PartialType"> implements DefaultTrait<InferPartial<D>> {
|
|
13
14
|
#private;
|
|
14
15
|
[Loose]: M;
|
|
15
|
-
constructor(spec: D, mode?: M);
|
|
16
|
+
constructor(spec: D, mode?: M, policy?: ObjectPolicy);
|
|
16
17
|
get name(): "partial";
|
|
17
18
|
get schema(): D;
|
|
18
19
|
default(value: (() => InferPartial<D>) | InferPartial<D>): DefaultType<this, InferPartial<D>>;
|
|
20
|
+
closed(): PartialType<D, M>;
|
|
21
|
+
min(limit: number): PartialType<D, M>;
|
|
19
22
|
parse(u: unknown, options?: ParseOptions): InferPartial<D>;
|
|
20
23
|
toJSON(): {
|
|
21
24
|
type: "partial";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cardinality from "#cardinality";
|
|
1
2
|
import DefaultType from "#DefaultType";
|
|
2
3
|
import E from "#errors";
|
|
3
4
|
import Loose from "#Loose";
|
|
@@ -10,11 +11,13 @@ import VirtualType from "#VirtualType";
|
|
|
10
11
|
import is from "@rcompat/is";
|
|
11
12
|
export default class PartialType extends VirtualType {
|
|
12
13
|
#spec;
|
|
14
|
+
#policy;
|
|
13
15
|
[Loose];
|
|
14
|
-
constructor(spec, mode) {
|
|
16
|
+
constructor(spec, mode, policy = {}) {
|
|
15
17
|
super();
|
|
16
18
|
this.#spec = spec;
|
|
17
19
|
this[Loose] = mode;
|
|
20
|
+
this.#policy = policy;
|
|
18
21
|
}
|
|
19
22
|
get name() {
|
|
20
23
|
return "partial";
|
|
@@ -25,6 +28,13 @@ export default class PartialType extends VirtualType {
|
|
|
25
28
|
default(value) {
|
|
26
29
|
return new DefaultType(this, value);
|
|
27
30
|
}
|
|
31
|
+
closed() {
|
|
32
|
+
return new PartialType(this.#spec, this[Loose], { ...this.#policy, closed: true });
|
|
33
|
+
}
|
|
34
|
+
min(limit) {
|
|
35
|
+
cardinality.min(limit);
|
|
36
|
+
return new PartialType(this.#spec, this[Loose], { ...this.#policy, minimum: limit });
|
|
37
|
+
}
|
|
28
38
|
parse(u, options = {}) {
|
|
29
39
|
const x = resolve(u);
|
|
30
40
|
if (!is.dict(x))
|
|
@@ -32,12 +42,22 @@ export default class PartialType extends VirtualType {
|
|
|
32
42
|
const input = x;
|
|
33
43
|
const out = {};
|
|
34
44
|
const issues = [];
|
|
45
|
+
if (this.#policy.closed === true) {
|
|
46
|
+
for (const key of Object.keys(input)) {
|
|
47
|
+
if (!Object.hasOwn(this.#spec, key)) {
|
|
48
|
+
throw E.unknown_property(input[key], next(key, options));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let count = 0;
|
|
35
53
|
for (const key of Object.keys(this.#spec)) {
|
|
36
54
|
// skip missing/undefined keys (partial semantics)
|
|
37
|
-
if (!(key
|
|
55
|
+
if (!Object.prototype.propertyIsEnumerable.call(input, key) ||
|
|
56
|
+
is.undefined(input[key]))
|
|
38
57
|
continue;
|
|
39
58
|
try {
|
|
40
59
|
const parsed = this.#spec[key].parse(input[key], next(key, options));
|
|
60
|
+
count++;
|
|
41
61
|
if (is.defined(parsed))
|
|
42
62
|
out[key] = parsed;
|
|
43
63
|
}
|
|
@@ -49,7 +69,7 @@ export default class PartialType extends VirtualType {
|
|
|
49
69
|
}
|
|
50
70
|
else {
|
|
51
71
|
// wrap non-ParseError into a properly-pathed issue at /<key>
|
|
52
|
-
const message = e &&
|
|
72
|
+
const message = e && is.string(e.message)
|
|
53
73
|
? e.message
|
|
54
74
|
: String(e);
|
|
55
75
|
issues.push({
|
|
@@ -63,6 +83,10 @@ export default class PartialType extends VirtualType {
|
|
|
63
83
|
}
|
|
64
84
|
if (issues.length > 0)
|
|
65
85
|
throw new ParseError(issues);
|
|
86
|
+
const minimum = this.#policy.minimum;
|
|
87
|
+
if (is.defined(minimum) && count < minimum) {
|
|
88
|
+
throw E.too_small(input, `min ${minimum} properties`, options[ParsedKey] ?? "");
|
|
89
|
+
}
|
|
66
90
|
return out;
|
|
67
91
|
}
|
|
68
92
|
toJSON() {
|
|
@@ -46,6 +46,7 @@ export default class PrimitiveType extends Type {
|
|
|
46
46
|
: this.#validators;
|
|
47
47
|
const loose = this[Loose] ?? $options[Loose] ?? false;
|
|
48
48
|
const $x = loose ? this[CoerceKey](x) : x;
|
|
49
|
+
// eslint-disable-next-line no-restricted-syntax -- expected type is dynamic
|
|
49
50
|
if (typeof $x !== this.name)
|
|
50
51
|
throw E.invalid_type($x, this.name, $options);
|
|
51
52
|
const base = $options[ParsedKey] ?? "";
|
|
@@ -65,7 +66,7 @@ export default class PrimitiveType extends Type {
|
|
|
65
66
|
throw new ParseError(rebased);
|
|
66
67
|
}
|
|
67
68
|
// not a ParseError - wrap with proper path
|
|
68
|
-
const message = e &&
|
|
69
|
+
const message = e && is.string(e.message)
|
|
69
70
|
? e.message
|
|
70
71
|
: String(e);
|
|
71
72
|
throw E.invalid_type(x, message, base);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import CoerceKey from "#CoerceKey";
|
|
2
|
+
import cardinality from "#cardinality";
|
|
2
3
|
import E from "#errors";
|
|
3
4
|
import Loose from "#Loose";
|
|
4
5
|
import ParsedKey from "#ParsedKey";
|
|
5
6
|
import ParseError from "#ParseError";
|
|
6
7
|
import PrimitiveType from "#PrimitiveType";
|
|
7
8
|
import resolve from "#resolve";
|
|
8
|
-
import S from "#schema-errors";
|
|
9
9
|
import Storable from "#Storable";
|
|
10
10
|
import email from "#validator/email";
|
|
11
11
|
import ends_with from "#validator/ends-with";
|
|
@@ -39,6 +39,7 @@ export default class StringType extends PrimitiveType {
|
|
|
39
39
|
: options;
|
|
40
40
|
const loose = this[Loose] ?? $options[Loose] ?? false;
|
|
41
41
|
const $x = loose ? this[CoerceKey](x) : x;
|
|
42
|
+
// eslint-disable-next-line no-restricted-syntax -- expected type is dynamic
|
|
42
43
|
if (typeof $x !== this.name)
|
|
43
44
|
throw E.invalid_type($x, this.name, $options);
|
|
44
45
|
let value = $x;
|
|
@@ -97,16 +98,15 @@ export default class StringType extends PrimitiveType {
|
|
|
97
98
|
return this.with({ validators: [ends_with(suffix, options)] });
|
|
98
99
|
}
|
|
99
100
|
min(limit, options) {
|
|
100
|
-
|
|
101
|
-
throw S.min_negative(limit);
|
|
101
|
+
cardinality.min(limit);
|
|
102
102
|
return this.with({ validators: [min(limit, options)] });
|
|
103
103
|
}
|
|
104
104
|
max(limit, options) {
|
|
105
|
-
|
|
106
|
-
throw S.max_negative(limit);
|
|
105
|
+
cardinality.max(limit);
|
|
107
106
|
return this.with({ validators: [max(limit, options)] });
|
|
108
107
|
}
|
|
109
108
|
length(from, to, options) {
|
|
109
|
+
cardinality.length(from, to);
|
|
110
110
|
return this.with({ validators: [length(from, to, options)] });
|
|
111
111
|
}
|
|
112
112
|
toJSON() {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare function min(limit: number): void;
|
|
2
|
+
declare function max(limit: number): void;
|
|
3
|
+
declare function length(from: number, to: number): void;
|
|
4
|
+
declare const cardinality: {
|
|
5
|
+
length: typeof length;
|
|
6
|
+
max: typeof max;
|
|
7
|
+
min: typeof min;
|
|
8
|
+
};
|
|
9
|
+
export default cardinality;
|
|
10
|
+
//# sourceMappingURL=cardinality.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import S from "#schema-errors";
|
|
2
|
+
import is from "@rcompat/is";
|
|
3
|
+
function min(limit) {
|
|
4
|
+
if (!is.finite(limit))
|
|
5
|
+
throw S.min_limit_not_finite(limit);
|
|
6
|
+
if (limit < 0)
|
|
7
|
+
throw S.min_negative(limit);
|
|
8
|
+
if (!is.int(limit))
|
|
9
|
+
throw S.min_limit_not_integer(limit);
|
|
10
|
+
}
|
|
11
|
+
function max(limit) {
|
|
12
|
+
if (!is.finite(limit))
|
|
13
|
+
throw S.max_limit_not_finite(limit);
|
|
14
|
+
if (limit < 0)
|
|
15
|
+
throw S.max_negative(limit);
|
|
16
|
+
if (!is.int(limit))
|
|
17
|
+
throw S.max_limit_not_integer(limit);
|
|
18
|
+
}
|
|
19
|
+
function length(from, to) {
|
|
20
|
+
if (!is.finite(from) || !is.finite(to)) {
|
|
21
|
+
throw S.length_not_finite(from, to);
|
|
22
|
+
}
|
|
23
|
+
if (from < 0 || to < 0)
|
|
24
|
+
throw S.length_not_positive(from, to);
|
|
25
|
+
if (!is.int(from) || !is.int(to))
|
|
26
|
+
throw S.length_not_integer(from, to);
|
|
27
|
+
if (from > to)
|
|
28
|
+
throw S.length_from_exceeds_to(from, to);
|
|
29
|
+
}
|
|
30
|
+
const cardinality = { length, max, min };
|
|
31
|
+
export default cardinality;
|
|
32
|
+
//# sourceMappingURL=cardinality.js.map
|
|
@@ -9,7 +9,7 @@ function epoch(n, digits) {
|
|
|
9
9
|
// for numbers we always treat as ms (no digits provided)
|
|
10
10
|
const ms = is.defined(digits) ? (digits >= 13 ? n : n * 1000) : n;
|
|
11
11
|
const date = new Date(ms);
|
|
12
|
-
return
|
|
12
|
+
return is.nan(date.getTime()) ? undefined : date;
|
|
13
13
|
}
|
|
14
14
|
function fromNumber(n) {
|
|
15
15
|
const date = epoch(n);
|
|
@@ -29,18 +29,18 @@ function fromString(raw) {
|
|
|
29
29
|
// ISO date-only -> interpret as UTC midnight
|
|
30
30
|
if (ISO_DATE.test(s)) {
|
|
31
31
|
const date = new Date(`${s}T00:00:00.000Z`);
|
|
32
|
-
return
|
|
32
|
+
return is.nan(date.getTime()) ? raw : date;
|
|
33
33
|
}
|
|
34
34
|
// ISO datetime *with* timezone
|
|
35
35
|
if (ISO_DATETIME_TZ.test(s)) {
|
|
36
36
|
const date = new Date(s);
|
|
37
|
-
return
|
|
37
|
+
return is.nan(date.getTime()) ? raw : date;
|
|
38
38
|
}
|
|
39
39
|
return raw;
|
|
40
40
|
}
|
|
41
41
|
export default function coerceDate(x) {
|
|
42
42
|
// already a Date
|
|
43
|
-
if (x
|
|
43
|
+
if (is.date(x))
|
|
44
44
|
return x;
|
|
45
45
|
// numbers are treated as epochs
|
|
46
46
|
if (is.finite(x))
|
package/lib/private/errors.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare function too_large(input: unknown, message: string, path?: JSONPointer):
|
|
|
8
8
|
declare function out_of_range(input: unknown, message: string, path?: JSONPointer): ParseError;
|
|
9
9
|
declare function duplicate(input: unknown, message: string, path?: JSONPointer): ParseError;
|
|
10
10
|
declare function not_in_set(input: unknown, message: string, path?: JSONPointer): ParseError;
|
|
11
|
+
declare function unknown_property(input: unknown, options: ParseOptions): ParseError;
|
|
11
12
|
declare function field(path: string, message: string): ParseError;
|
|
12
13
|
declare const _default: {
|
|
13
14
|
invalid_type: typeof invalid_type;
|
|
@@ -17,6 +18,7 @@ declare const _default: {
|
|
|
17
18
|
out_of_range: typeof out_of_range;
|
|
18
19
|
duplicate: typeof duplicate;
|
|
19
20
|
not_in_set: typeof not_in_set;
|
|
21
|
+
unknown_property: typeof unknown_property;
|
|
20
22
|
field: typeof field;
|
|
21
23
|
};
|
|
22
24
|
export default _default;
|
package/lib/private/errors.js
CHANGED
|
@@ -40,6 +40,14 @@ function duplicate(input, message, path = "") {
|
|
|
40
40
|
function not_in_set(input, message, path = "") {
|
|
41
41
|
return new ParseError([{ type: "not_in_set", input, message, path }]);
|
|
42
42
|
}
|
|
43
|
+
function unknown_property(input, options) {
|
|
44
|
+
return new ParseError([{
|
|
45
|
+
type: "unknown_property",
|
|
46
|
+
input,
|
|
47
|
+
message: "unknown property",
|
|
48
|
+
path: options[ParsedKey] ?? "",
|
|
49
|
+
}]);
|
|
50
|
+
}
|
|
43
51
|
function field(path, message) {
|
|
44
52
|
return new ParseError([{
|
|
45
53
|
type: "invalid_format",
|
|
@@ -56,6 +64,7 @@ export default {
|
|
|
56
64
|
out_of_range,
|
|
57
65
|
duplicate,
|
|
58
66
|
not_in_set,
|
|
67
|
+
unknown_property,
|
|
59
68
|
field,
|
|
60
69
|
};
|
|
61
70
|
//# sourceMappingURL=errors.js.map
|
package/lib/private/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export type { default as UUIDV4Type } from "#UUIDV4Type";
|
|
|
43
43
|
export type { default as UUIDV7Type } from "#UUIDV7Type";
|
|
44
44
|
import async_schema from "#async";
|
|
45
45
|
import type NormalizeSchema from "#NormalizeSchema";
|
|
46
|
+
import parseable from "#parseable";
|
|
46
47
|
import type Schema from "#Schema";
|
|
47
48
|
/**
|
|
48
49
|
* Create a schema.
|
|
@@ -146,6 +147,7 @@ declare namespace schema {
|
|
|
146
147
|
export var _18: import("#UUIDType").default;
|
|
147
148
|
export { _18 as uuid };
|
|
148
149
|
export var error: (path: string, message: string) => import("./ParseError.js").default;
|
|
150
|
+
export { parseable };
|
|
149
151
|
export { loose };
|
|
150
152
|
export { strict };
|
|
151
153
|
}
|
package/lib/private/index.js
CHANGED
|
@@ -29,6 +29,7 @@ import number from "#number";
|
|
|
29
29
|
import object from "#object";
|
|
30
30
|
import omit from "#omit";
|
|
31
31
|
import partial from "#partial";
|
|
32
|
+
import parseable from "#parseable";
|
|
32
33
|
import pure from "#pure";
|
|
33
34
|
import record from "#record";
|
|
34
35
|
import string from "#string";
|
|
@@ -194,6 +195,7 @@ schema.unknown = unknown.vanilla;
|
|
|
194
195
|
schema.url = url.vanilla;
|
|
195
196
|
schema.uuid = uuid.vanilla;
|
|
196
197
|
schema.error = E.field;
|
|
198
|
+
schema.parseable = parseable;
|
|
197
199
|
schema.loose = loose;
|
|
198
200
|
schema.strict = strict;
|
|
199
201
|
export default schema;
|
package/lib/private/normalize.js
CHANGED
package/lib/private/resolve.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import parseable from "#parseable";
|
|
1
2
|
import is from "@rcompat/is";
|
|
2
|
-
import symbol from "@rcompat/symbol";
|
|
3
|
-
const { parse } = symbol;
|
|
4
3
|
export default function resolve(x) {
|
|
5
|
-
return is.object(x) &&
|
|
4
|
+
return is.object(x) && parseable in x
|
|
5
|
+
? x[parseable]()
|
|
6
|
+
: x;
|
|
6
7
|
}
|
|
7
8
|
//# sourceMappingURL=resolve.js.map
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
declare const errors: {
|
|
2
|
-
unique_subtype_not_primitive: (subtype: string) => import("@rcompat/error").
|
|
3
|
-
min_negative: (limit: number) => import("@rcompat/error").
|
|
4
|
-
max_negative: (limit: number) => import("@rcompat/error").
|
|
5
|
-
length_not_finite: (from: number, to: number) => import("@rcompat/error").
|
|
6
|
-
length_not_positive: (from: number, to: number) => import("@rcompat/error").
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
unique_subtype_not_primitive: (subtype: string) => import("@rcompat/error").CodeError<"unique_subtype_not_primitive">;
|
|
3
|
+
min_negative: (limit: number) => import("@rcompat/error").CodeError<"min_negative">;
|
|
4
|
+
max_negative: (limit: number) => import("@rcompat/error").CodeError<"max_negative">;
|
|
5
|
+
length_not_finite: (from: number, to: number) => import("@rcompat/error").CodeError<"length_not_finite">;
|
|
6
|
+
length_not_positive: (from: number, to: number) => import("@rcompat/error").CodeError<"length_not_positive">;
|
|
7
|
+
length_not_integer: (from: number, to: number) => import("@rcompat/error").CodeError<"length_not_integer">;
|
|
8
|
+
length_from_exceeds_to: (from: number, to: number) => import("@rcompat/error").CodeError<"length_from_exceeds_to">;
|
|
9
|
+
min_limit_not_finite: (limit: number) => import("@rcompat/error").CodeError<"min_limit_not_finite">;
|
|
10
|
+
min_limit_not_integer: (limit: number) => import("@rcompat/error").CodeError<"min_limit_not_integer">;
|
|
11
|
+
max_limit_not_finite: (limit: number) => import("@rcompat/error").CodeError<"max_limit_not_finite">;
|
|
12
|
+
max_limit_not_integer: (limit: number) => import("@rcompat/error").CodeError<"max_limit_not_integer">;
|
|
13
|
+
extend_key_collision: (key: string) => import("@rcompat/error").CodeError<"extend_key_collision">;
|
|
14
|
+
union_at_least_two_members: () => import("@rcompat/error").CodeError<"union_at_least_two_members">;
|
|
15
|
+
enum_invalid_key: (key: string) => import("@rcompat/error").CodeError<"enum_invalid_key">;
|
|
13
16
|
};
|
|
14
17
|
export type Code = keyof typeof errors;
|
|
15
18
|
export declare const Code: { [K in Code]: K; };
|
|
@@ -2,13 +2,16 @@ import error from "@rcompat/error";
|
|
|
2
2
|
const t = error.template;
|
|
3
3
|
const errors = error.coded({
|
|
4
4
|
unique_subtype_not_primitive: (subtype) => t `unique: subtype ${subtype} must be primitive`,
|
|
5
|
-
min_negative: (limit) => t `min: ${limit} must be
|
|
6
|
-
max_negative: (limit) => t `max: ${limit} must be
|
|
5
|
+
min_negative: (limit) => t `min: ${limit} must be nonnegative`,
|
|
6
|
+
max_negative: (limit) => t `max: ${limit} must be nonnegative`,
|
|
7
7
|
length_not_finite: (from, to) => t `length: ${from} and ${to} must be finite numbers`,
|
|
8
|
-
length_not_positive: (from, to) => t `length: ${from} and ${to} must be
|
|
9
|
-
|
|
8
|
+
length_not_positive: (from, to) => t `length: ${from} and ${to} must be nonnegative`,
|
|
9
|
+
length_not_integer: (from, to) => t `length: ${from} and ${to} must be integers`,
|
|
10
|
+
length_from_exceeds_to: (from, to) => t `length: ${from} must not exceed ${to}`,
|
|
10
11
|
min_limit_not_finite: (limit) => t `min: limit ${limit} must be a finite number`,
|
|
12
|
+
min_limit_not_integer: (limit) => t `min: limit ${limit} must be an integer`,
|
|
11
13
|
max_limit_not_finite: (limit) => t `max: limit ${limit} must be a finite number`,
|
|
14
|
+
max_limit_not_integer: (limit) => t `max: limit ${limit} must be an integer`,
|
|
12
15
|
extend_key_collision: (key) => t `extend: key ${key} already exists and cannot be overridden`,
|
|
13
16
|
union_at_least_two_members: () => t `union type must have at least two members`,
|
|
14
17
|
enum_invalid_key: (key) => t `enum: key ${key} must be SCREAMING_SNAKE_CASE`,
|
package/lib/private/test.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ type IssueMatcher = {
|
|
|
9
9
|
type Shortcuts = {
|
|
10
10
|
[K in IssueType]: (inputs: unknown[], path?: JSONPointer) => void;
|
|
11
11
|
};
|
|
12
|
-
declare const _default: import("@rcompat/test
|
|
12
|
+
declare const _default: import("@rcompat/test").ExtendedTest<{
|
|
13
13
|
parse_issue: (type: IssueType, inputs: Input[]) => void;
|
|
14
14
|
parse_issues(input: unknown, matchers: IssueMatcher[]): void;
|
|
15
15
|
} & Shortcuts>;
|
package/lib/private/test.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import E from "#errors";
|
|
2
|
-
import S from "#schema-errors";
|
|
3
2
|
import message from "#validator/message";
|
|
4
3
|
import is from "@rcompat/is";
|
|
5
4
|
export default function length(from, to, options) {
|
|
6
|
-
if (!is.finite(from) || !is.finite(to))
|
|
7
|
-
throw S.length_not_finite(from, to);
|
|
8
|
-
if (from < 0 || to < 0)
|
|
9
|
-
throw S.length_not_positive(from, to);
|
|
10
|
-
if (from > to)
|
|
11
|
-
throw S.length_from_exceeds_to(from, to);
|
|
12
5
|
const format = message(options, () => "length out of range");
|
|
13
6
|
return (x) => {
|
|
14
7
|
if (!is.string(x) && !is.array(x)) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import is from "@rcompat/is";
|
|
2
2
|
export default function message(options, fallback) {
|
|
3
3
|
const option = is.dict(options) ? options.message : options;
|
|
4
|
+
// Preserve the generic callback signature; is.function narrows to
|
|
5
|
+
// UnknownFunction and loses the string return type.
|
|
6
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
4
7
|
return (value) => typeof option === "function"
|
|
5
8
|
? option(value)
|
|
6
9
|
: option ?? fallback(value);
|
package/package.json
CHANGED
|
@@ -1,36 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "pema",
|
|
3
|
-
"version": "0.11.0",
|
|
4
|
-
"description": "Primate schema validation",
|
|
5
|
-
"homepage": "https://primate.run/docs/validation",
|
|
6
2
|
"bugs": "https://github.com/primate-run/primate/issues",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "https://github.com/primate-run/primate",
|
|
12
|
-
"directory": "packages/pema"
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"/lib/**/*.js",
|
|
16
|
-
"/lib/**/*.d.ts",
|
|
17
|
-
"!/**/*.spec.*"
|
|
18
|
-
],
|
|
19
3
|
"dependencies": {
|
|
20
|
-
"@rcompat/assert": "^0.
|
|
21
|
-
"@rcompat/cli": "^0.
|
|
22
|
-
"@rcompat/error": "^0.
|
|
23
|
-
"@rcompat/is": "^0.
|
|
24
|
-
"@rcompat/symbol": "^0.3.0"
|
|
4
|
+
"@rcompat/assert": "^0.16.0",
|
|
5
|
+
"@rcompat/cli": "^0.28.0",
|
|
6
|
+
"@rcompat/error": "^0.11.0",
|
|
7
|
+
"@rcompat/is": "^0.14.0"
|
|
25
8
|
},
|
|
9
|
+
"description": "Primate schema validation",
|
|
26
10
|
"devDependencies": {
|
|
27
|
-
"@rcompat/type": "^0.
|
|
28
|
-
},
|
|
29
|
-
"imports": {
|
|
30
|
-
"#*": {
|
|
31
|
-
"@primate/source": "./src/private/*.ts",
|
|
32
|
-
"default": "./lib/private/*.js"
|
|
33
|
-
}
|
|
11
|
+
"@rcompat/type": "^0.19.0"
|
|
34
12
|
},
|
|
35
13
|
"exports": {
|
|
36
14
|
".": {
|
|
@@ -42,11 +20,33 @@
|
|
|
42
20
|
"default": "./lib/public/*.js"
|
|
43
21
|
}
|
|
44
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"/lib/**/*.js",
|
|
25
|
+
"/lib/**/*.d.ts",
|
|
26
|
+
"!/**/*.spec.*"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://primate.run/docs/pema",
|
|
29
|
+
"imports": {
|
|
30
|
+
"#*": {
|
|
31
|
+
"@primate/source": "./src/private/*.ts",
|
|
32
|
+
"default": "./lib/private/*.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"name": "pema",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/primate-run/primate",
|
|
40
|
+
"directory": "packages/pema"
|
|
41
|
+
},
|
|
45
42
|
"scripts": {
|
|
46
|
-
"build": "
|
|
43
|
+
"build": "flog run clean && tsc",
|
|
47
44
|
"clean": "rm -rf ./lib",
|
|
48
45
|
"lint": "eslint .",
|
|
49
46
|
"lint:fix": "eslint . --fix",
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
}
|
|
47
|
+
"prepublishOnly": "flog run build",
|
|
48
|
+
"test": "proby"
|
|
49
|
+
},
|
|
50
|
+
"type": "module",
|
|
51
|
+
"version": "0.12.1"
|
|
52
|
+
}
|