pema 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- if (limit < 0)
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
- if (limit < 0)
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): 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";
@@ -19,6 +19,9 @@ export default class DateType extends BuiltinType {
19
19
  get datatype() {
20
20
  return "datetime";
21
21
  }
22
+ now() {
23
+ return this.default(() => new Date());
24
+ }
22
25
  toJSON() {
23
26
  return Storable.serialize(this);
24
27
  }
@@ -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
@@ -4,13 +4,28 @@ 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 x.every(json_with_undefined_object_values);
10
- if (is.dict(x)) {
11
- return Object.values(x).every(value => is.undefined(value) || json_with_undefined_object_values(value));
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
- return is.json(x);
27
+ ancestors.delete(x);
28
+ return valid;
14
29
  }
15
30
  export default class JSONType extends Storable {
16
31
  #inner;
@@ -0,0 +1,6 @@
1
+ type ObjectPolicy = {
2
+ closed?: boolean;
3
+ minimum?: number;
4
+ };
5
+ export type { ObjectPolicy as default };
6
+ //# sourceMappingURL=ObjectPolicy.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ObjectPolicy.js.map
@@ -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
- for (const k in this.#properties) {
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 in input) || is.undefined(input[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 && typeof e.message === "string"
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 && typeof e.message === "string"
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
- if (limit < 0)
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
- if (limit < 0)
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 Number.isNaN(date.getTime()) ? undefined : date;
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 Number.isNaN(date.getTime()) ? raw : date;
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 Number.isNaN(date.getTime()) ? raw : date;
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 instanceof Date)
43
+ if (is.date(x))
44
44
  return x;
45
45
  // numbers are treated as epochs
46
46
  if (is.finite(x))
@@ -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;
@@ -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
@@ -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
  }
@@ -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;
@@ -19,7 +19,7 @@ export default function normalize(x) {
19
19
  if (is.newable(x)) {
20
20
  return new ConstructorType(x);
21
21
  }
22
- if (Array.isArray(x)) {
22
+ if (is.array(x)) {
23
23
  const schemas = x;
24
24
  return schemas.length === 1
25
25
  ? new ArrayType(normalize(schemas[0]))
@@ -0,0 +1,3 @@
1
+ declare const parseable: unique symbol;
2
+ export default parseable;
3
+ //# sourceMappingURL=parseable.d.ts.map
@@ -0,0 +1,3 @@
1
+ const parseable = Symbol.for("pema:parseable");
2
+ export default parseable;
3
+ //# sourceMappingURL=parseable.js.map
@@ -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) && parse in x ? x[parse]() : 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").TemplateError;
3
- min_negative: (limit: number) => import("@rcompat/error").TemplateError;
4
- max_negative: (limit: number) => import("@rcompat/error").TemplateError;
5
- length_not_finite: (from: number, to: number) => import("@rcompat/error").TemplateError;
6
- length_not_positive: (from: number, to: number) => import("@rcompat/error").TemplateError;
7
- length_from_exceeds_to: (from: number, to: number) => import("@rcompat/error").TemplateError;
8
- min_limit_not_finite: (limit: number) => import("@rcompat/error").TemplateError;
9
- max_limit_not_finite: (limit: number) => import("@rcompat/error").TemplateError;
10
- extend_key_collision: (key: string) => import("@rcompat/error").TemplateError;
11
- union_at_least_two_members: () => import("@rcompat/error").TemplateError;
12
- enum_invalid_key: (key: string) => import("@rcompat/error").TemplateError;
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 positive`,
6
- max_negative: (limit) => t `max: ${limit} must be positive`,
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 positive`,
9
- length_from_exceeds_to: (from, to) => t `length: ${from} must be lower than ${to}`,
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`,
@@ -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/index").ExtendedTest<{
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>;
@@ -9,6 +9,7 @@ const issue_types = [
9
9
  "out_of_range",
10
10
  "not_in_set",
11
11
  "duplicate",
12
+ "unknown_property",
12
13
  ];
13
14
  export default test.extend((assert, subject) => {
14
15
  const parse_issue = (type, inputs) => {
@@ -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.10.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.14.0",
21
- "@rcompat/cli": "^0.25.0",
22
- "@rcompat/error": "^0.9.0",
23
- "@rcompat/is": "^0.12.1",
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.18.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": "pnpm run clean && tsc",
43
+ "build": "flog run clean && tsc",
47
44
  "clean": "rm -rf ./lib",
48
45
  "lint": "eslint .",
49
46
  "lint:fix": "eslint . --fix",
50
- "test": "npx proby"
51
- }
52
- }
47
+ "prepublishOnly": "flog run build",
48
+ "test": "proby"
49
+ },
50
+ "type": "module",
51
+ "version": "0.12.0"
52
+ }