decoders 2.3.0-test4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,110 +1,45 @@
1
- // src/_utils.ts
2
- var INDENT = " ";
3
- function lazyval(value) {
4
- return typeof value === "function" ? value() : value;
1
+ // src/lib/utils.ts
2
+ function isNumber(value) {
3
+ return typeof value === "number";
5
4
  }
6
- function subtract(xs, ys) {
7
- const result = /* @__PURE__ */ new Set();
8
- for (const x of xs) {
9
- if (!ys.has(x)) {
10
- result.add(x);
11
- }
12
- }
13
- return result;
5
+ function isString(value) {
6
+ return typeof value === "string";
7
+ }
8
+ function isBigInt(value) {
9
+ return typeof value === "bigint";
14
10
  }
15
- function asDate(value) {
16
- return !!value && Object.prototype.toString.call(value) === "[object Date]" && !isNaN(value) ? value : null;
11
+ function isDate(value) {
12
+ return !!value && Object.prototype.toString.call(value) === "[object Date]" && !isNaN(value);
17
13
  }
18
14
  function isPojo(value) {
19
15
  return value !== null && value !== void 0 && typeof value === "object" && // This still seems to be the only reliable way to determine whether
20
16
  // something is a pojo... ¯\_(ツ)_/¯
21
17
  Object.prototype.toString.call(value) === "[object Object]";
22
18
  }
23
- function isMultiline(s) {
24
- return s.indexOf("\n") >= 0;
25
- }
26
- function indent(s, prefix = INDENT) {
27
- if (isMultiline(s)) {
28
- return s.split("\n").map((line) => `${prefix}${line}`).join("\n");
29
- } else {
30
- return `${prefix}${s}`;
31
- }
32
- }
33
- function summarize(ann, keypath = []) {
34
- const result = [];
35
- if (ann.type === "array") {
36
- const items = ann.items;
37
- let index = 0;
38
- for (const ann2 of items) {
39
- for (const item of summarize(ann2, [...keypath, index++])) {
40
- result.push(item);
41
- }
42
- }
43
- } else if (ann.type === "object") {
44
- const fields = ann.fields;
45
- for (const key of Object.keys(fields)) {
46
- const value = fields[key];
47
- for (const item of summarize(value, [...keypath, key])) {
48
- result.push(item);
49
- }
50
- }
51
- }
52
- const text = ann.text;
53
- if (!text) {
54
- return result;
55
- }
56
- let prefix;
57
- if (keypath.length === 0) {
58
- prefix = "";
59
- } else if (keypath.length === 1) {
60
- prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${JSON.stringify(keypath[0])}: `;
61
- } else {
62
- prefix = `Value at keypath ${keypath.map(String).join(".")}: `;
63
- }
64
- return [...result, `${prefix}${text}`];
65
- }
66
19
 
67
- // src/annotate.ts
20
+ // src/core/annotate.ts
68
21
  var _register = /* @__PURE__ */ new WeakSet();
69
22
  function brand(ann) {
70
23
  _register.add(ann);
71
24
  return ann;
72
25
  }
73
- function object(fields, text) {
26
+ function makeObjectAnn(fields, text) {
74
27
  return brand({ type: "object", fields, text });
75
28
  }
76
- function array(items, text) {
77
- return brand({
78
- type: "array",
79
- items,
80
- text
81
- });
29
+ function makeArrayAnn(items, text) {
30
+ return brand({ type: "array", items, text });
82
31
  }
83
- function func(text) {
84
- return brand({
85
- type: "function",
86
- text
87
- });
32
+ function makeFunctionAnn(text) {
33
+ return brand({ type: "function", text });
88
34
  }
89
- function unknown(value, text) {
90
- return brand({
91
- type: "unknown",
92
- value,
93
- text
94
- });
35
+ function makeUnknownAnn(value, text) {
36
+ return brand({ type: "unknown", value, text });
95
37
  }
96
- function scalar(value, text) {
97
- return brand({
98
- type: "scalar",
99
- value,
100
- text
101
- });
38
+ function makeScalarAnn(value, text) {
39
+ return brand({ type: "scalar", value, text });
102
40
  }
103
- function circularRef(text) {
104
- return brand({
105
- type: "circular-ref",
106
- text
107
- });
41
+ function makeCircularRefAnn(text) {
42
+ return brand({ type: "circular-ref", text });
108
43
  }
109
44
  function updateText(annotation, text) {
110
45
  if (text !== void 0) {
@@ -114,52 +49,54 @@ function updateText(annotation, text) {
114
49
  }
115
50
  }
116
51
  function merge(objAnnotation, fields) {
117
- const newFields = { ...objAnnotation.fields, ...fields };
118
- return object(newFields, objAnnotation.text);
52
+ const newFields = new Map([...objAnnotation.fields, ...fields]);
53
+ return makeObjectAnn(newFields, objAnnotation.text);
119
54
  }
120
- function asAnnotation(thing) {
121
- return typeof thing === "object" && thing !== null && _register.has(thing) ? thing : void 0;
55
+ function isAnnotation(thing) {
56
+ return _register.has(thing);
122
57
  }
123
- function annotateArray(value, text, seen) {
124
- seen.add(value);
125
- const items = value.map((v) => annotate(v, void 0, seen));
126
- return array(items, text);
58
+ function annotateArray(arr, text, seen) {
59
+ seen.add(arr);
60
+ const items = [];
61
+ for (const value of arr) {
62
+ items.push(annotate(value, void 0, seen));
63
+ }
64
+ return makeArrayAnn(items, text);
127
65
  }
128
66
  function annotateObject(obj, text, seen) {
129
67
  seen.add(obj);
130
- const fields = {};
68
+ const fields = /* @__PURE__ */ new Map();
131
69
  for (const key of Object.keys(obj)) {
132
70
  const value = obj[key];
133
- fields[key] = annotate(value, void 0, seen);
71
+ fields.set(key, annotate(value, void 0, seen));
134
72
  }
135
- return object(fields, text);
73
+ return makeObjectAnn(fields, text);
136
74
  }
137
75
  function annotate(value, text, seen) {
138
76
  if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "symbol" || typeof value.getMonth === "function") {
139
- return scalar(value, text);
77
+ return makeScalarAnn(value, text);
140
78
  }
141
- const ann = asAnnotation(value);
142
- if (ann) {
143
- return updateText(ann, text);
79
+ if (isAnnotation(value)) {
80
+ return updateText(value, text);
144
81
  }
145
82
  if (Array.isArray(value)) {
146
83
  if (seen.has(value)) {
147
- return circularRef(text);
84
+ return makeCircularRefAnn(text);
148
85
  } else {
149
86
  return annotateArray(value, text, seen);
150
87
  }
151
88
  }
152
89
  if (isPojo(value)) {
153
90
  if (seen.has(value)) {
154
- return circularRef(text);
91
+ return makeCircularRefAnn(text);
155
92
  } else {
156
93
  return annotateObject(value, text, seen);
157
94
  }
158
95
  }
159
96
  if (typeof value === "function") {
160
- return func(text);
97
+ return makeFunctionAnn(text);
161
98
  }
162
- return unknown(value, text);
99
+ return makeUnknownAnn(value, text);
163
100
  }
164
101
  function public_annotate(value, text) {
165
102
  return annotate(value, text, /* @__PURE__ */ new WeakSet());
@@ -168,7 +105,56 @@ function public_annotateObject(obj, text) {
168
105
  return annotateObject(obj, text, /* @__PURE__ */ new WeakSet());
169
106
  }
170
107
 
171
- // src/format.ts
108
+ // src/lib/text.ts
109
+ var INDENT = " ";
110
+ function isMultiline(s) {
111
+ return s.indexOf("\n") >= 0;
112
+ }
113
+ function indent(s, prefix = INDENT) {
114
+ if (isMultiline(s)) {
115
+ return s.split("\n").map((line) => `${prefix}${line}`).join("\n");
116
+ } else {
117
+ return `${prefix}${s}`;
118
+ }
119
+ }
120
+ var quotePattern = /'/g;
121
+ function quote(value) {
122
+ return typeof value === "string" ? "'" + value.replace(quotePattern, "\\'") + "'" : JSON.stringify(value);
123
+ }
124
+
125
+ // src/core/format.ts
126
+ function summarize(ann, keypath = []) {
127
+ const result = [];
128
+ if (ann.type === "array") {
129
+ const items = ann.items;
130
+ let index = 0;
131
+ for (const ann2 of items) {
132
+ for (const item of summarize(ann2, [...keypath, index++])) {
133
+ result.push(item);
134
+ }
135
+ }
136
+ } else if (ann.type === "object") {
137
+ const fields = ann.fields;
138
+ for (const [key, value] of fields) {
139
+ for (const item of summarize(value, [...keypath, key])) {
140
+ result.push(item);
141
+ }
142
+ }
143
+ }
144
+ const text = ann.text;
145
+ if (!text) {
146
+ return result;
147
+ }
148
+ let prefix;
149
+ if (keypath.length === 0) {
150
+ prefix = "";
151
+ } else if (keypath.length === 1) {
152
+ prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${quote(keypath[0])}: `;
153
+ } else {
154
+ prefix = `Value at keypath ${quote(keypath.map(String).join("."))}: `;
155
+ }
156
+ return [...result, `${prefix}${text}`];
157
+ }
172
158
  function serializeString(s, width = 80) {
173
159
  let ser = JSON.stringify(s);
174
160
  if (ser.length <= width) {
@@ -195,13 +181,11 @@ function serializeArray(annotation, prefix) {
195
181
  }
196
182
  function serializeObject(annotation, prefix) {
197
183
  const { fields } = annotation;
198
- const fieldNames = Object.keys(fields);
199
- if (fieldNames.length === 0) {
184
+ if (fields.size === 0) {
200
185
  return "{}";
201
186
  }
202
187
  const result = [];
203
- for (const key of fieldNames) {
204
- const valueAnnotation = fields[key];
188
+ for (const [key, valueAnnotation] of fields) {
205
189
  const kser = serializeValue(key);
206
190
  const valPrefix = `${prefix}${INDENT}${" ".repeat(kser.length + 2)}`;
207
191
  const [vser, vann] = serializeAnnotation(valueAnnotation, `${prefix}${INDENT}`);
@@ -215,16 +199,15 @@ function serializeObject(annotation, prefix) {
215
199
  function serializeValue(value) {
216
200
  if (typeof value === "string") {
217
201
  return serializeString(value);
218
- } else if (typeof value === "number" || typeof value === "boolean") {
202
+ } else if (typeof value === "number" || typeof value === "boolean" || typeof value === "symbol") {
219
203
  return value.toString();
220
204
  } else if (value === null) {
221
205
  return "null";
222
206
  } else if (value === void 0) {
223
207
  return "undefined";
224
208
  } else {
225
- const valueAsDate = asDate(value);
226
- if (valueAsDate !== null) {
227
- return `new Date(${JSON.stringify(valueAsDate.toISOString())})`;
209
+ if (isDate(value)) {
210
+ return `new Date(${quote(value.toISOString())})`;
228
211
  } else if (value instanceof Date) {
229
212
  return "(Invalid Date)";
230
213
  } else {
@@ -268,7 +251,7 @@ function formatShort(ann) {
268
251
  return summarize(ann, []).join("\n");
269
252
  }
270
253
 
271
- // src/result.ts
254
+ // src/core/Result.ts
272
255
  function ok(value) {
273
256
  return { ok: true, value, error: void 0 };
274
257
  }
@@ -276,7 +259,7 @@ function err(error) {
276
259
  return { ok: false, value: void 0, error };
277
260
  }
278
261
 
279
- // src/Decoder.ts
262
+ // src/core/Decoder.ts
280
263
  function noThrow(fn) {
281
264
  return (t) => {
282
265
  try {
@@ -303,7 +286,7 @@ function define(fn) {
303
286
  return fn(
304
287
  blob,
305
288
  ok,
306
- (msg) => err(typeof msg === "string" ? public_annotate(blob, msg) : msg)
289
+ (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg))
307
290
  );
308
291
  }
309
292
  function verify(blob, formatter = formatInline) {
@@ -333,14 +316,20 @@ function define(fn) {
333
316
  }
334
317
  function then(next) {
335
318
  return define((blob, ok2, err2) => {
336
- const result = decode(blob);
337
- return result.ok ? next(result.value, ok2, err2) : result;
319
+ const r1 = decode(blob);
320
+ if (!r1.ok)
321
+ return r1;
322
+ const r2 = isDecoder(next) ? next : next(r1.value, ok2, err2);
323
+ return isDecoder(r2) ? r2.decode(r1.value) : r2;
338
324
  });
339
325
  }
326
+ function pipe(next) {
327
+ return then(next);
328
+ }
340
329
  function reject(rejectFn) {
341
- return then((value2, ok2, err2) => {
342
- const errmsg = rejectFn(value2);
343
- return errmsg === null ? ok2(value2) : err2(typeof errmsg === "string" ? public_annotate(value2, errmsg) : errmsg);
330
+ return then((blob, ok2, err2) => {
331
+ const errmsg = rejectFn(blob);
332
+ return errmsg === null ? ok2(blob) : err2(typeof errmsg === "string" ? public_annotate(blob, errmsg) : errmsg);
344
333
  });
345
334
  }
346
335
  function describe(message) {
@@ -353,13 +342,7 @@ function define(fn) {
353
342
  }
354
343
  });
355
344
  }
356
- function peek_UNSTABLE(next) {
357
- return define((blob, ok2, err2) => {
358
- const result = decode(blob);
359
- return result.ok ? next([blob, result.value], ok2, err2) : result;
360
- });
361
- }
362
- return Object.freeze({
345
+ return brand2({
363
346
  verify,
364
347
  value,
365
348
  decode,
@@ -368,30 +351,137 @@ function define(fn) {
368
351
  reject,
369
352
  describe,
370
353
  then,
371
- // EXPERIMENTAL - please DO NOT rely on this method
372
- peek_UNSTABLE
354
+ pipe
355
+ });
356
+ }
357
+ var _register2 = /* @__PURE__ */ new WeakSet();
358
+ function brand2(decoder) {
359
+ _register2.add(decoder);
360
+ return decoder;
361
+ }
362
+ function isDecoder(thing) {
363
+ return _register2.has(thing);
364
+ }
365
+
366
+ // src/arrays.ts
367
+ var poja = define((blob, ok2, err2) => {
368
+ if (!Array.isArray(blob)) {
369
+ return err2("Must be an array");
370
+ }
371
+ return ok2(blob);
372
+ });
373
+ function all(items, blobs, ok2, err2) {
374
+ const results = [];
375
+ for (let index = 0; index < items.length; ++index) {
376
+ const result = items[index];
377
+ if (result.ok) {
378
+ results.push(result.value);
379
+ } else {
380
+ const ann = result.error;
381
+ const clone = blobs.slice();
382
+ clone.splice(
383
+ index,
384
+ 1,
385
+ public_annotate(ann, ann.text ? `${ann.text} (at index ${index})` : `index ${index}`)
386
+ );
387
+ return err2(public_annotate(clone));
388
+ }
389
+ }
390
+ return ok2(results);
391
+ }
392
+ function array(decoder) {
393
+ const decodeFn = decoder.decode;
394
+ return poja.then((blobs, ok2, err2) => {
395
+ const results = blobs.map(decodeFn);
396
+ return all(results, blobs, ok2, err2);
397
+ });
398
+ }
399
+ function isNonEmpty(arr) {
400
+ return arr.length > 0;
401
+ }
402
+ function nonEmptyArray(decoder) {
403
+ return array(decoder).refine(isNonEmpty, "Must be non-empty array");
404
+ }
405
+ var ntuple = (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
406
+ function tuple(...decoders) {
407
+ return ntuple(decoders.length).then((blobs, ok2, err2) => {
408
+ let allOk = true;
409
+ const rvs = decoders.map((decoder, i) => {
410
+ const blob = blobs[i];
411
+ const result = decoder.decode(blob);
412
+ if (result.ok) {
413
+ return result.value;
414
+ } else {
415
+ allOk = false;
416
+ return result.error;
417
+ }
418
+ });
419
+ if (allOk) {
420
+ return ok2(rvs);
421
+ } else {
422
+ return err2(public_annotate(rvs));
423
+ }
424
+ });
425
+ }
426
+
427
+ // src/misc.ts
428
+ function instanceOf(klass) {
429
+ return define(
430
+ (blob, ok2, err2) => blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
431
+ );
432
+ }
433
+ function lazy(decoderFn) {
434
+ return define((blob) => decoderFn().decode(blob));
435
+ }
436
+ function prep(mapperFn, decoder) {
437
+ return define((originalInput, _, err2) => {
438
+ let blob;
439
+ try {
440
+ blob = mapperFn(originalInput);
441
+ } catch (e) {
442
+ return err2(
443
+ public_annotate(
444
+ originalInput,
445
+ // istanbul ignore next
446
+ e instanceof Error ? e.message : String(e)
447
+ )
448
+ );
449
+ }
450
+ const r = decoder.decode(blob);
451
+ return r.ok ? r : err2(public_annotate(originalInput, r.error.text));
373
452
  });
374
453
  }
375
454
 
376
- // src/lib/objects.ts
455
+ // src/lib/set-methods.ts
456
+ function difference(xs, ys) {
457
+ const result = /* @__PURE__ */ new Set();
458
+ for (const x of xs) {
459
+ if (!ys.has(x)) {
460
+ result.add(x);
461
+ }
462
+ }
463
+ return result;
464
+ }
465
+
466
+ // src/objects.ts
377
467
  var pojo = define(
378
468
  (blob, ok2, err2) => isPojo(blob) ? ok2(blob) : err2("Must be an object")
379
469
  );
380
- function object2(decodersByKey) {
381
- const knownKeys = new Set(Object.keys(decodersByKey));
470
+ function object(decoders) {
471
+ const knownKeys = new Set(Object.keys(decoders));
382
472
  return pojo.then((plainObj, ok2, err2) => {
383
473
  const actualKeys = new Set(Object.keys(plainObj));
384
- const missingKeys = subtract(knownKeys, actualKeys);
385
- const record = {};
474
+ const missingKeys = difference(knownKeys, actualKeys);
475
+ const record2 = {};
386
476
  let errors = null;
387
- for (const key of Object.keys(decodersByKey)) {
388
- const decoder = decodersByKey[key];
477
+ for (const key of Object.keys(decoders)) {
478
+ const decoder = decoders[key];
389
479
  const rawValue = plainObj[key];
390
480
  const result = decoder.decode(rawValue);
391
481
  if (result.ok) {
392
482
  const value = result.value;
393
483
  if (value !== void 0) {
394
- record[key] = value;
484
+ record2[key] = value;
395
485
  }
396
486
  missingKeys.delete(key);
397
487
  } else {
@@ -400,9 +490,9 @@ function object2(decodersByKey) {
400
490
  missingKeys.add(key);
401
491
  } else {
402
492
  if (errors === null) {
403
- errors = {};
493
+ errors = /* @__PURE__ */ new Map();
404
494
  }
405
- errors[key] = ann;
495
+ errors.set(key, ann);
406
496
  }
407
497
  }
408
498
  }
@@ -412,32 +502,29 @@ function object2(decodersByKey) {
412
502
  objAnn = merge(objAnn, errors);
413
503
  }
414
504
  if (missingKeys.size > 0) {
415
- const errMsg = Array.from(missingKeys).map((key) => `"${key}"`).join(", ");
505
+ const errMsg = Array.from(missingKeys).map(quote).join(", ");
416
506
  const pluralized = missingKeys.size > 1 ? "keys" : "key";
417
507
  objAnn = updateText(objAnn, `Missing ${pluralized}: ${errMsg}`);
418
508
  }
419
509
  return err2(objAnn);
420
510
  }
421
- return ok2(record);
511
+ return ok2(record2);
422
512
  });
423
513
  }
424
- function exact(decodersByKey) {
425
- const allowedKeys = new Set(Object.keys(decodersByKey));
514
+ function exact(decoders) {
515
+ const allowedKeys = new Set(Object.keys(decoders));
426
516
  const checked = pojo.reject((plainObj) => {
427
517
  const actualKeys = new Set(Object.keys(plainObj));
428
- const extraKeys = subtract(actualKeys, allowedKeys);
429
- return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).join(", ")}` : (
430
- // Don't reject
431
- null
432
- );
518
+ const extraKeys = difference(actualKeys, allowedKeys);
519
+ return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}` : null;
433
520
  });
434
- return checked.then(object2(decodersByKey).decode);
521
+ return checked.pipe(object(decoders));
435
522
  }
436
- function inexact(decodersByKey) {
437
- return pojo.then((plainObj) => {
523
+ function inexact(decoders) {
524
+ return pojo.pipe((plainObj) => {
438
525
  const allkeys = new Set(Object.keys(plainObj));
439
- const decoder = object2(decodersByKey).transform((safepart) => {
440
- const safekeys = new Set(Object.keys(decodersByKey));
526
+ return object(decoders).transform((safepart) => {
527
+ const safekeys = new Set(Object.keys(decoders));
441
528
  for (const k of safekeys)
442
529
  allkeys.add(k);
443
530
  const rv = {};
@@ -453,80 +540,16 @@ function inexact(decodersByKey) {
453
540
  }
454
541
  return rv;
455
542
  });
456
- return decoder.decode(plainObj);
457
- });
458
- }
459
- function dict(decoder) {
460
- return pojo.then((plainObj, ok2, err2) => {
461
- let rv = {};
462
- let errors = null;
463
- for (const key of Object.keys(plainObj)) {
464
- const value = plainObj[key];
465
- const result = decoder.decode(value);
466
- if (result.ok) {
467
- if (errors === null) {
468
- rv[key] = result.value;
469
- }
470
- } else {
471
- rv = {};
472
- if (errors === null) {
473
- errors = {};
474
- }
475
- errors[key] = result.error;
476
- }
477
- }
478
- if (errors !== null) {
479
- return err2(merge(public_annotateObject(plainObj), errors));
480
- } else {
481
- return ok2(rv);
482
- }
483
543
  });
484
544
  }
485
- function mapping(decoder) {
486
- return dict(decoder).transform(
487
- (obj) => new Map(
488
- // This is effectively Object.entries(obj), but in a way that Flow
489
- // will know the types are okay
490
- Object.keys(obj).map((key) => [key, obj[key]])
491
- )
492
- );
493
- }
494
545
 
495
- // src/lib/utilities.ts
496
- function instanceOf(klass) {
497
- return define(
498
- (blob, ok2, err2) => blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
499
- );
500
- }
501
- function lazy(decoderFn) {
502
- return define((blob) => decoderFn().decode(blob));
503
- }
504
- function prep(mapperFn, decoder) {
505
- return define((originalInput, _, err2) => {
506
- let blob;
507
- try {
508
- blob = mapperFn(originalInput);
509
- } catch (e) {
510
- return err2(
511
- public_annotate(
512
- originalInput,
513
- // istanbul ignore next
514
- e instanceof Error ? e.message : String(e)
515
- )
516
- );
517
- }
518
- const r = decoder.decode(blob);
519
- return r.ok ? r : err2(public_annotate(originalInput, r.error.text));
520
- });
521
- }
522
-
523
- // src/lib/unions.ts
546
+ // src/unions.ts
524
547
  var EITHER_PREFIX = "Either:\n";
525
548
  function itemize(s) {
526
549
  return `-${indent(s).substring(1)}`;
527
550
  }
528
551
  function nest(errText) {
529
- return errText.startsWith(EITHER_PREFIX) ? errText.substr(EITHER_PREFIX.length) : itemize(errText);
552
+ return errText.startsWith(EITHER_PREFIX) ? errText.substring(EITHER_PREFIX.length) : itemize(errText);
530
553
  }
531
554
  function either(...decoders) {
532
555
  if (decoders.length === 0) {
@@ -552,28 +575,48 @@ function oneOf(constants) {
552
575
  if (winner !== void 0) {
553
576
  return ok2(winner);
554
577
  }
555
- return err2(
556
- `Must be one of ${constants.map((value) => JSON.stringify(value)).join(", ")}`
557
- );
578
+ return err2(`Must be one of ${constants.map((value) => quote(value)).join(", ")}`);
558
579
  });
559
580
  }
581
+ function enum_(enumObj) {
582
+ const values = Object.values(enumObj);
583
+ if (!values.some(isNumber)) {
584
+ return oneOf(values);
585
+ } else {
586
+ const nums = values.filter(isNumber);
587
+ const ignore = new Set(nums.map((val) => enumObj[val]));
588
+ const strings = values.filter(isString).filter((val) => !ignore.has(val));
589
+ return oneOf([...nums, ...strings]);
590
+ }
591
+ }
560
592
  function taggedUnion(field, mapping2) {
561
- const base = object2({
593
+ const scout = object({
562
594
  [field]: prep(String, oneOf(Object.keys(mapping2)))
563
595
  }).transform((o) => o[field]);
564
- return base.peek_UNSTABLE(([blob, key]) => {
565
- const decoder = mapping2[key];
566
- return decoder.decode(blob);
596
+ return select(
597
+ scout,
598
+ // peek...
599
+ (key) => mapping2[key]
600
+ // ...then select
601
+ );
602
+ }
603
+ function select(scout, selectFn) {
604
+ return define((blob) => {
605
+ const result = scout.decode(blob);
606
+ return result.ok ? selectFn(result.value).decode(blob) : result;
567
607
  });
568
608
  }
569
609
 
570
- // src/lib/basics.ts
610
+ // src/basics.ts
611
+ function lazyval(value) {
612
+ return typeof value === "function" ? value() : value;
613
+ }
571
614
  var null_ = constant(null);
572
615
  var undefined_ = constant(void 0);
573
616
  var nullish_ = define(
574
- (blob, ok2, err2) => blob == null ? ok2(blob) : (
575
- // Combine error message into a single line for readability
576
- err2("Must be undefined or null")
617
+ (blob, ok2, err2) => (
618
+ // Equiv to either(undefined_, null_), but combined for better error message
619
+ blob == null ? ok2(blob) : err2("Must be undefined or null")
577
620
  )
578
621
  );
579
622
  function optional(decoder, defaultValue) {
@@ -591,7 +634,7 @@ function nullish(decoder, defaultValue) {
591
634
  }
592
635
  function constant(value) {
593
636
  return define(
594
- (blob, ok2, err2) => blob === value ? ok2(value) : err2(`Must be ${JSON.stringify(value)}`)
637
+ (blob, ok2, err2) => blob === value ? ok2(value) : err2(`Must be ${typeof value === "symbol" ? String(value) : quote(value)}`)
595
638
  );
596
639
  }
597
640
  function always(value) {
@@ -604,99 +647,73 @@ function never(msg) {
604
647
  }
605
648
  var fail = never;
606
649
  var hardcoded = always;
607
- var unknown2 = define((blob, ok2, _) => ok2(blob));
608
- var mixed = unknown2;
650
+ var unknown = define((blob, ok2, _) => ok2(blob));
651
+ var mixed = unknown;
609
652
 
610
- // src/lib/arrays.ts
611
- var poja = define((blob, ok2, err2) => {
612
- if (!Array.isArray(blob)) {
613
- return err2("Must be an array");
614
- }
615
- return ok2(blob);
653
+ // src/booleans.ts
654
+ var boolean = define((blob, ok2, err2) => {
655
+ return typeof blob === "boolean" ? ok2(blob) : err2("Must be boolean");
616
656
  });
617
- function all(items, blobs, ok2, err2) {
618
- const results = [];
619
- for (let index = 0; index < items.length; ++index) {
620
- const result = items[index];
621
- if (result.ok) {
622
- results.push(result.value);
623
- } else {
624
- const ann = result.error;
625
- const clone = blobs.slice();
626
- clone.splice(
627
- index,
628
- 1,
629
- public_annotate(ann, ann.text ? `${ann.text} (at index ${index})` : `index ${index}`)
630
- );
631
- return err2(public_annotate(clone));
632
- }
633
- }
634
- return ok2(results);
635
- }
636
- function array2(decoder) {
637
- const decodeFn = decoder.decode;
638
- return poja.then((blobs, ok2, err2) => {
639
- const results = blobs.map(decodeFn);
640
- return all(results, blobs, ok2, err2);
641
- });
642
- }
643
- function isNonEmpty(arr) {
644
- return arr.length > 0;
645
- }
646
- function nonEmptyArray(decoder) {
647
- return array2(decoder).refine(isNonEmpty, "Must be non-empty array");
648
- }
649
- function set(decoder) {
650
- return array2(decoder).transform((items) => new Set(items));
651
- }
652
- var ntuple = (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
653
- function tuple(...decoders) {
654
- return ntuple(decoders.length).then((blobs, ok2, err2) => {
655
- let allOk = true;
656
- const rvs = decoders.map((decoder, i) => {
657
- const blob = blobs[i];
658
- const result = decoder.decode(blob);
657
+ var truthy = define((blob, ok2, _) => ok2(!!blob));
658
+
659
+ // src/collections.ts
660
+ function record(fst, snd) {
661
+ const keyDecoder = snd !== void 0 ? fst : void 0;
662
+ const valueDecoder = snd !== void 0 ? snd : fst;
663
+ return pojo.then((input, ok2, err2) => {
664
+ let rv = {};
665
+ const errors = /* @__PURE__ */ new Map();
666
+ for (const key of Object.keys(input)) {
667
+ const value = input[key];
668
+ const keyResult = keyDecoder?.decode(key);
669
+ if (keyResult?.ok === false) {
670
+ return err2(
671
+ public_annotate(input, `Invalid key ${quote(key)}: ${formatShort(keyResult.error)}`)
672
+ );
673
+ }
674
+ const k = keyResult?.value ?? key;
675
+ const result = valueDecoder.decode(value);
659
676
  if (result.ok) {
660
- return result.value;
677
+ if (errors.size === 0) {
678
+ rv[k] = result.value;
679
+ }
661
680
  } else {
662
- allOk = false;
663
- return result.error;
681
+ errors.set(key, result.error);
682
+ rv = {};
664
683
  }
665
- });
666
- if (allOk) {
667
- return ok2(rvs);
684
+ }
685
+ if (errors.size > 0) {
686
+ return err2(merge(public_annotateObject(input), errors));
668
687
  } else {
669
- return err2(public_annotate(rvs));
688
+ return ok2(rv);
670
689
  }
671
690
  });
672
691
  }
692
+ var dict = record;
693
+ function setFromArray(decoder) {
694
+ return array(decoder).transform((items) => new Set(items));
695
+ }
696
+ var set = setFromArray;
697
+ function mapping(decoder) {
698
+ return record(decoder).transform((obj) => new Map(Object.entries(obj)));
699
+ }
673
700
 
674
- // src/lib/numbers.ts
675
- var anyNumber = define(
676
- (blob, ok2, err2) => typeof blob === "number" ? ok2(blob) : err2("Must be number")
677
- );
678
- var number = anyNumber.refine(
679
- (n) => Number.isFinite(n),
680
- "Number must be finite"
681
- );
682
- var integer = number.refine(
683
- (n) => Number.isInteger(n),
684
- "Number must be an integer"
685
- );
686
- var positiveNumber = number.refine((n) => n >= 0, "Number must be positive").transform(Math.abs);
687
- var positiveInteger = integer.refine((n) => n >= 0, "Number must be positive").transform(Math.abs);
688
-
689
- // src/lib/booleans.ts
690
- var boolean = define((blob, ok2, err2) => {
691
- return typeof blob === "boolean" ? ok2(blob) : err2("Must be boolean");
692
- });
693
- var truthy = define((blob, ok2, _) => ok2(!!blob));
694
- var numericBoolean = number.transform((n) => !!n);
701
+ // src/lib/size-options.ts
702
+ function bySizeOptions(options) {
703
+ const size = options?.size;
704
+ const min = size ?? options?.min;
705
+ const max = size ?? options?.max;
706
+ const atLeast = min === max ? "" : "at least ";
707
+ const atMost = min === max ? "" : "at most ";
708
+ const tooShort = min !== void 0 && `Too short, must be ${atLeast}${min} chars`;
709
+ const tooLong = max !== void 0 && `Too long, must be ${atMost}${max} chars`;
710
+ return tooShort && tooLong ? (s) => s.length < min ? tooShort : s.length > max ? tooLong : null : tooShort ? (s) => s.length < min ? tooShort : null : tooLong ? (s) => s.length > max ? tooLong : null : () => null;
711
+ }
695
712
 
696
- // src/lib/strings.ts
713
+ // src/strings.ts
697
714
  var url_re = /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/;
698
715
  var string = define(
699
- (blob, ok2, err2) => typeof blob === "string" ? ok2(blob) : err2("Must be string")
716
+ (blob, ok2, err2) => isString(blob) ? ok2(blob) : err2("Must be string")
700
717
  );
701
718
  var nonEmptyString = regex(/\S/, "Must be non-empty string");
702
719
  function regex(regex2, msg) {
@@ -715,6 +732,15 @@ var httpsUrl = url.refine(
715
732
  (value) => value.protocol === "https:",
716
733
  "Must be an HTTPS URL"
717
734
  );
735
+ var identifier = regex(
736
+ /^[a-z_][a-z0-9_]*$/i,
737
+ "Must be valid identifier"
738
+ );
739
+ function nanoid(options) {
740
+ return regex(/^[a-z0-9_-]+$/i, "Must be nano ID").reject(
741
+ bySizeOptions(options ?? { size: 21 })
742
+ );
743
+ }
718
744
  var uuid = regex(
719
745
  /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
720
746
  "Must be uuid"
@@ -727,12 +753,17 @@ var uuidv4 = (
727
753
  // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
728
754
  uuid.refine((value) => value[14] === "4", "Must be uuidv4")
729
755
  );
756
+ var decimal = regex(/^[0-9]+$/, "Must only contain digits");
757
+ var hexadecimal = regex(
758
+ /^[0-9a-f]+$/i,
759
+ "Must only contain hexadecimal digits"
760
+ );
761
+ var numeric = decimal.transform(Number);
730
762
 
731
- // src/lib/dates.ts
763
+ // src/dates.ts
732
764
  var iso8601_re = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:?\d{2})$/;
733
765
  var date = define((blob, ok2, err2) => {
734
- const date2 = asDate(blob);
735
- return date2 !== null ? ok2(date2) : err2("Must be a Date");
766
+ return isDate(blob) ? ok2(blob) : err2("Must be a Date");
736
767
  });
737
768
  var iso8601 = (
738
769
  // Input itself needs to match the ISO8601 regex...
@@ -747,10 +778,35 @@ var iso8601 = (
747
778
  }
748
779
  )
749
780
  );
781
+ var datelike = either(date, iso8601).describe("Must be a Date or date string");
782
+
783
+ // src/numbers.ts
784
+ var anyNumber = define(
785
+ (blob, ok2, err2) => isNumber(blob) ? ok2(blob) : err2("Must be number")
786
+ );
787
+ var number = anyNumber.refine(
788
+ (n) => Number.isFinite(n),
789
+ "Number must be finite"
790
+ );
791
+ var integer = number.refine(
792
+ (n) => Number.isInteger(n),
793
+ "Number must be an integer"
794
+ );
795
+ var positiveNumber = number.refine(
796
+ (n) => n >= 0 && !Object.is(n, -0),
797
+ "Number must be positive"
798
+ );
799
+ var positiveInteger = integer.refine(
800
+ (n) => n >= 0 && !Object.is(n, -0),
801
+ "Number must be positive"
802
+ );
803
+ var bigint = define(
804
+ (blob, ok2, err2) => isBigInt(blob) ? ok2(blob) : err2("Must be bigint")
805
+ );
750
806
 
751
- // src/lib/json.ts
752
- var jsonObject = lazy(() => dict(json));
753
- var jsonArray = lazy(() => array2(json));
807
+ // src/json.ts
808
+ var jsonObject = lazy(() => record(json));
809
+ var jsonArray = lazy(() => array(json));
754
810
  var json = either(
755
811
  null_,
756
812
  string,
@@ -762,21 +818,27 @@ var json = either(
762
818
  export {
763
819
  always,
764
820
  anyNumber,
765
- array2 as array,
821
+ array,
822
+ bigint,
766
823
  boolean,
767
824
  constant,
768
825
  date,
826
+ datelike,
827
+ decimal,
769
828
  define,
770
829
  dict,
771
830
  either,
772
831
  email,
832
+ enum_,
773
833
  err,
774
834
  exact,
775
835
  fail,
776
836
  formatInline,
777
837
  formatShort,
778
838
  hardcoded,
839
+ hexadecimal,
779
840
  httpsUrl,
841
+ identifier,
780
842
  inexact,
781
843
  instanceOf,
782
844
  integer,
@@ -788,6 +850,7 @@ export {
788
850
  mapping,
789
851
  maybe,
790
852
  mixed,
853
+ nanoid,
791
854
  never,
792
855
  nonEmptyArray,
793
856
  nonEmptyString,
@@ -795,8 +858,8 @@ export {
795
858
  nullable,
796
859
  nullish,
797
860
  number,
798
- numericBoolean,
799
- object2 as object,
861
+ numeric,
862
+ object,
800
863
  ok,
801
864
  oneOf,
802
865
  optional,
@@ -805,17 +868,19 @@ export {
805
868
  positiveInteger,
806
869
  positiveNumber,
807
870
  prep,
871
+ record,
808
872
  regex,
873
+ select,
809
874
  set,
875
+ setFromArray,
810
876
  string,
811
877
  taggedUnion,
812
878
  truthy,
813
879
  tuple,
814
880
  undefined_,
815
- unknown2 as unknown,
881
+ unknown,
816
882
  url,
817
883
  uuid,
818
884
  uuidv1,
819
885
  uuidv4
820
886
  };
821
- //# sourceMappingURL=index.js.map