@constructive-io/coerce 0.2.0 → 0.3.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/README.md CHANGED
@@ -66,6 +66,7 @@ Then the text-carried and named shapes — what an env var, a query string or a
66
66
  | `asIntegerIn` | `requireIntegerIn` | an integer within `{ min, max }`, inclusive |
67
67
  | `asPort` | `requirePort` | a port: an integer in `1..65535`, from a number or a string |
68
68
  | `asBigInt` | `requireBigInt` | a `bigint`, an integer, or a digit string |
69
+ | `asBigIntString` | `requireBigIntString` | the same, answered as canonical digits — the wire form of an `int8` |
69
70
  | `asUrl` | `requireUrl` | an absolute URL — a scheme is required |
70
71
  | `asHostname` | `requireHostname` | a hostname or IP address — no scheme, port or path |
71
72
  | `asEmail` | `requireEmail` | an email address |
package/coerce.d.ts CHANGED
@@ -48,6 +48,14 @@ export declare const asBoolean: (value: unknown) => boolean | null;
48
48
  * shrinks.
49
49
  */
50
50
  export declare const asStringArray: (value: unknown) => string[] | null;
51
+ /**
52
+ * An array whose every entry coerces through `element`, else `null`. One bad
53
+ * entry rejects the whole array — a partially-valid list is a partially-wrong
54
+ * answer.
55
+ */
56
+ export declare const asArrayOf: <T>(value: unknown, element: (entry: unknown) => T | null) => T[] | null;
57
+ /** {@link asArrayOf}, throwing {@link CoerceError} when absent. */
58
+ export declare const requireArrayOf: <T>(value: unknown, element: (entry: unknown) => T | null, label: string, expected?: string) => T[];
51
59
  /**
52
60
  * A list of non-empty strings from either an array (see
53
61
  * {@link asStringArray}) or a delimited string, else `null`.
@@ -104,6 +112,16 @@ export declare const asPort: (value: unknown) => number | null;
104
112
  * its way through JSON, where it cannot survive as a `number`.
105
113
  */
106
114
  export declare const asBigInt: (value: unknown) => bigint | null;
115
+ /**
116
+ * A whole number in its canonical text form, else `null` — {@link asBigInt}
117
+ * spelled the way a 64-bit value travels.
118
+ *
119
+ * Text is the wire format for `int8`: node-postgres returns the type as a
120
+ * string, JSON carries it as one, and a `number` would silently lose identity
121
+ * past 2^53. Every accepted shape is normalised through `BigInt`, so `'007'`
122
+ * and `7` both answer `'7'` and two ids compare as themselves.
123
+ */
124
+ export declare const asBigIntString: (value: unknown) => string | null;
107
125
  /**
108
126
  * An absolute URL, returned **verbatim**, else `null`. A scheme is required, so
109
127
  * `'example.com'` — which is a path, and a classic way to fetch the wrong
@@ -163,6 +181,8 @@ export declare const requireNumericInteger: (value: unknown, label: string) => n
163
181
  export declare const requirePort: (value: unknown, label: string) => number;
164
182
  /** {@link asBigInt}, throwing {@link CoerceError} when absent. */
165
183
  export declare const requireBigInt: (value: unknown, label: string) => bigint;
184
+ /** {@link asBigIntString}, throwing {@link CoerceError} when absent. */
185
+ export declare const requireBigIntString: (value: unknown, label: string) => string;
166
186
  /** {@link asUrl}, throwing {@link CoerceError} when absent. */
167
187
  export declare const requireUrl: (value: unknown, label: string) => string;
168
188
  /** {@link asHostname}, throwing {@link CoerceError} when absent. */
package/coerce.js CHANGED
@@ -22,7 +22,7 @@
22
22
  * which keeps this package usable off the request path.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.requireOneOf = exports.asOneOf = exports.requireIntegerIn = exports.requireNumberIn = exports.requireJson = exports.requireDuration = exports.requireUuid = exports.requireEmail = exports.requireHostname = exports.requireUrl = exports.requireBigInt = exports.requirePort = exports.requireNumericInteger = exports.requireNumeric = exports.requireIsoString = exports.requireDate = exports.requireRecord = exports.requireStringList = exports.requireStringArray = exports.requireBoolean = exports.requireInteger = exports.requireNumber = exports.requireString = exports.asJson = exports.asDuration = exports.asUuid = exports.asEmail = exports.asHostname = exports.asUrl = exports.asBigInt = exports.asPort = exports.asIntegerIn = exports.asNumberIn = exports.asNumericInteger = exports.asNumeric = exports.asIsoString = exports.asDate = exports.asRecord = exports.asStringList = exports.asStringArray = exports.asBoolean = exports.asInteger = exports.asNumber = exports.asString = exports.CoerceError = void 0;
25
+ exports.requireOneOf = exports.asOneOf = exports.requireIntegerIn = exports.requireNumberIn = exports.requireJson = exports.requireDuration = exports.requireUuid = exports.requireEmail = exports.requireHostname = exports.requireUrl = exports.requireBigIntString = exports.requireBigInt = exports.requirePort = exports.requireNumericInteger = exports.requireNumeric = exports.requireIsoString = exports.requireDate = exports.requireRecord = exports.requireStringList = exports.requireStringArray = exports.requireBoolean = exports.requireInteger = exports.requireNumber = exports.requireString = exports.asJson = exports.asDuration = exports.asUuid = exports.asEmail = exports.asHostname = exports.asUrl = exports.asBigIntString = exports.asBigInt = exports.asPort = exports.asIntegerIn = exports.asNumberIn = exports.asNumericInteger = exports.asNumeric = exports.asIsoString = exports.asDate = exports.asRecord = exports.asStringList = exports.requireArrayOf = exports.asArrayOf = exports.asStringArray = exports.asBoolean = exports.asInteger = exports.asNumber = exports.asString = exports.CoerceError = void 0;
26
26
  /** A value that was required but absent or of the wrong shape. */
27
27
  class CoerceError extends Error {
28
28
  /** What the caller was asking for, e.g. `'message_id'`. */
@@ -85,6 +85,32 @@ const asStringArray = (value) => {
85
85
  return strings;
86
86
  };
87
87
  exports.asStringArray = asStringArray;
88
+ /**
89
+ * An array whose every entry coerces through `element`, else `null`. One bad
90
+ * entry rejects the whole array — a partially-valid list is a partially-wrong
91
+ * answer.
92
+ */
93
+ const asArrayOf = (value, element) => {
94
+ if (!Array.isArray(value))
95
+ return null;
96
+ const entries = [];
97
+ for (const item of value) {
98
+ const coerced = element(item);
99
+ if (coerced === null)
100
+ return null;
101
+ entries.push(coerced);
102
+ }
103
+ return entries;
104
+ };
105
+ exports.asArrayOf = asArrayOf;
106
+ /** {@link asArrayOf}, throwing {@link CoerceError} when absent. */
107
+ const requireArrayOf = (value, element, label, expected = 'an array of valid entries') => {
108
+ const entries = (0, exports.asArrayOf)(value, element);
109
+ if (entries === null)
110
+ throw new CoerceError(label, expected);
111
+ return entries;
112
+ };
113
+ exports.requireArrayOf = requireArrayOf;
88
114
  /**
89
115
  * A list of non-empty strings from either an array (see
90
116
  * {@link asStringArray}) or a delimited string, else `null`.
@@ -183,6 +209,17 @@ const asBigInt = (value) => {
183
209
  };
184
210
  exports.asBigInt = asBigInt;
185
211
  const INTEGER_TEXT = /^[+-]?\d+$/;
212
+ /**
213
+ * A whole number in its canonical text form, else `null` — {@link asBigInt}
214
+ * spelled the way a 64-bit value travels.
215
+ *
216
+ * Text is the wire format for `int8`: node-postgres returns the type as a
217
+ * string, JSON carries it as one, and a `number` would silently lose identity
218
+ * past 2^53. Every accepted shape is normalised through `BigInt`, so `'007'`
219
+ * and `7` both answer `'7'` and two ids compare as themselves.
220
+ */
221
+ const asBigIntString = (value) => (0, exports.asBigInt)(value)?.toString() ?? null;
222
+ exports.asBigIntString = asBigIntString;
186
223
  /**
187
224
  * An absolute URL, returned **verbatim**, else `null`. A scheme is required, so
188
225
  * `'example.com'` — which is a path, and a classic way to fetch the wrong
@@ -316,6 +353,8 @@ exports.requireNumericInteger = require_(exports.asNumericInteger, 'an integer')
316
353
  exports.requirePort = require_(exports.asPort, 'a port in 1..65535');
317
354
  /** {@link asBigInt}, throwing {@link CoerceError} when absent. */
318
355
  exports.requireBigInt = require_(exports.asBigInt, 'a whole number');
356
+ /** {@link asBigIntString}, throwing {@link CoerceError} when absent. */
357
+ exports.requireBigIntString = require_(exports.asBigIntString, 'a whole number');
319
358
  /** {@link asUrl}, throwing {@link CoerceError} when absent. */
320
359
  exports.requireUrl = require_(exports.asUrl, 'an absolute URL');
321
360
  /** {@link asHostname}, throwing {@link CoerceError} when absent. */
package/esm/coerce.js CHANGED
@@ -76,6 +76,30 @@ export const asStringArray = (value) => {
76
76
  }
77
77
  return strings;
78
78
  };
79
+ /**
80
+ * An array whose every entry coerces through `element`, else `null`. One bad
81
+ * entry rejects the whole array — a partially-valid list is a partially-wrong
82
+ * answer.
83
+ */
84
+ export const asArrayOf = (value, element) => {
85
+ if (!Array.isArray(value))
86
+ return null;
87
+ const entries = [];
88
+ for (const item of value) {
89
+ const coerced = element(item);
90
+ if (coerced === null)
91
+ return null;
92
+ entries.push(coerced);
93
+ }
94
+ return entries;
95
+ };
96
+ /** {@link asArrayOf}, throwing {@link CoerceError} when absent. */
97
+ export const requireArrayOf = (value, element, label, expected = 'an array of valid entries') => {
98
+ const entries = asArrayOf(value, element);
99
+ if (entries === null)
100
+ throw new CoerceError(label, expected);
101
+ return entries;
102
+ };
79
103
  /**
80
104
  * A list of non-empty strings from either an array (see
81
105
  * {@link asStringArray}) or a delimited string, else `null`.
@@ -164,6 +188,16 @@ export const asBigInt = (value) => {
164
188
  return str !== undefined && INTEGER_TEXT.test(str) ? BigInt(str) : null;
165
189
  };
166
190
  const INTEGER_TEXT = /^[+-]?\d+$/;
191
+ /**
192
+ * A whole number in its canonical text form, else `null` — {@link asBigInt}
193
+ * spelled the way a 64-bit value travels.
194
+ *
195
+ * Text is the wire format for `int8`: node-postgres returns the type as a
196
+ * string, JSON carries it as one, and a `number` would silently lose identity
197
+ * past 2^53. Every accepted shape is normalised through `BigInt`, so `'007'`
198
+ * and `7` both answer `'7'` and two ids compare as themselves.
199
+ */
200
+ export const asBigIntString = (value) => asBigInt(value)?.toString() ?? null;
167
201
  /**
168
202
  * An absolute URL, returned **verbatim**, else `null`. A scheme is required, so
169
203
  * `'example.com'` — which is a path, and a classic way to fetch the wrong
@@ -291,6 +325,8 @@ export const requireNumericInteger = require_(asNumericInteger, 'an integer');
291
325
  export const requirePort = require_(asPort, 'a port in 1..65535');
292
326
  /** {@link asBigInt}, throwing {@link CoerceError} when absent. */
293
327
  export const requireBigInt = require_(asBigInt, 'a whole number');
328
+ /** {@link asBigIntString}, throwing {@link CoerceError} when absent. */
329
+ export const requireBigIntString = require_(asBigIntString, 'a whole number');
294
330
  /** {@link asUrl}, throwing {@link CoerceError} when absent. */
295
331
  export const requireUrl = require_(asUrl, 'an absolute URL');
296
332
  /** {@link asHostname}, throwing {@link CoerceError} when absent. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/coerce",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Coerce untrusted unknown values into narrow TypeScript types",
6
6
  "main": "index.js",
@@ -36,5 +36,5 @@
36
36
  "validation",
37
37
  "typescript"
38
38
  ],
39
- "gitHead": "10ee73e43c2277405c0e19423e2075baef6c2755"
39
+ "gitHead": "ce544846eafd96280fac2607f873960392ae2c89"
40
40
  }