@dbx-tools/shared-core 0.6.61 → 0.6.63

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
@@ -206,9 +206,41 @@ const isRunnable = predicate
206
206
  short-circuits. `predicate.create()` returns composable predicates with `and`,
207
207
  `or`, and `negate`, used heavily by the projen engine.
208
208
 
209
- ## Dates And Durations
209
+ ## Serializable Values And Stable Identities
210
210
 
211
211
  ```ts
212
+ if (object.isSerializableValue(requestBody)) {
213
+ await bus.broadcast("orders", { type: "order.updated", body: requestBody });
214
+ }
215
+
216
+ const key = object.toStableKey({ schema: "billing", version: 2 });
217
+ ```
218
+
219
+ `object.isSerializableValue()` answers "does this survive a JSON round trip
220
+ unchanged", which is stricter than `JSON.stringify` not throwing: that succeeds
221
+ while turning a `Date` into a string, `NaN` into `null`, and a `Map` into `{}`,
222
+ and while dropping `undefined`. It narrows to `SerializableValue` instead of
223
+ throwing, so it doubles as the validator for a request body. Type a serializing
224
+ boundary as `SerializableValue` to catch the same mistakes at compile time.
225
+
226
+ `object.toStableKey()` canonicalizes a value so an identity can be derived from
227
+ it. Object key order does not affect the result, but types and structure do:
228
+ `1` and `"1"` differ, and so do `["a", "bc"]` and `["ab", "c"]`. It throws on a
229
+ cycle, a non-finite number, or a function/symbol rather than returning an
230
+ identity two callers could disagree about. `@dbx-tools/postgres` derives both its
231
+ advisory-lock ids and its notification channel names through it. Use
232
+ `hash.fnvHash()` instead when a short collision-tolerant digest is enough — its
233
+ canonicalizer is looser and folds every `Date` onto one token.
234
+
235
+ ## Numbers, Dates, And Durations
236
+
237
+ ```ts
238
+ object.toNumber("1,000"); // 1000
239
+ object.toNumber(" -2.5 "); // -2.5
240
+ object.toNumber("12.5 %"); // 0.125
241
+ object.toNumber(""); // undefined - `Number("")` would be 0
242
+ object.toNumber("12px"); // undefined
243
+
212
244
  object.toDate("2026-08-02"); // a date or ISO instant
213
245
  object.toDate("1785697899"); // epoch SECONDS (not the year 1785697899)
214
246
  object.toDate(1785697899000); // epoch millis
@@ -219,14 +251,24 @@ object.toDate("nope"); // undefined - never throws
219
251
  object.toDuration("1 hour 30 minutes"); // 5_400_000
220
252
  object.toDuration("2ms"); // 2
221
253
  object.toDuration("-7d"); // -604_800_000
254
+ object.toDuration("2026-08-02"); // ms from now until that instant
222
255
  ```
223
256
 
224
- Both are for values typed by HAND into env vars, CLI flags, and config files, so
225
- they are deliberately lenient: whitespace between amount and unit is optional,
226
- units are case-insensitive, and plurals and abbreviations are equivalent (`2ms`
227
- === `2 milliseconds`, `1h` === `1 hr` === `1 Hour`). Signs make a duration an
228
- OFFSET, which is what lets `toDate` read `-30d` / `12 hours ago` / `in 45s` as
229
- instants.
257
+ All three are for values typed by HAND into env vars, CLI flags, and config
258
+ files, so they are deliberately lenient: whitespace between amount and unit is
259
+ optional, units are case-insensitive, and plurals and abbreviations are
260
+ equivalent (`2ms` === `2 milliseconds`, `1h` === `1 hr` === `1 Hour`). Signs make
261
+ a duration an OFFSET, which is what lets `toDate` read `-30d` / `12 hours ago` /
262
+ `in 45s` as instants.
263
+
264
+ `toNumber` is the base the other two are built on, and the reason to reach for it
265
+ over `Number(value)` is that `Number` invents values: `""`, `null`, `[]`, and a
266
+ whitespace string all become `0`, so every caller has to re-check the result.
267
+ `toNumber` returns `undefined` instead, and accepts the spellings a person
268
+ actually types — digit-group separators, a bare fraction, a trailing point, an
269
+ exponent, a trailing percent. `ToNumberOptions` disables the `separators` and
270
+ `percent` leniencies for a string whose other characters carry meaning; a SQL
271
+ cell uses that so `"1,000"` in a text column stays the string the query returned.
230
272
 
231
273
  `toDate` exists rather than `new Date(value)` because a bare epoch usually
232
274
  arrives as a STRING (`date +%s`, a JSON field, a copied log line) and
@@ -238,6 +280,26 @@ than `1`. Neither function throws - like `toBoolean` they return `undefined`, so
238
280
  the caller decides whether a bad value is fatal, a warning, or a fallback
239
281
  (`@dbx-tools/tunnel`'s `TUNNEL_AUTH_SESSION_CUTOFF` warns and carries on).
240
282
 
283
+ ### Dates And Durations Are Inverses
284
+
285
+ Each function falls back to the other, so one field accepts both readings: a
286
+ duration reaching `toDate` resolves against now, and a date reaching
287
+ `toDuration` becomes the signed offset from now (`date - now`, so a past instant
288
+ is negative). Each tries its own reading first — `toDate` runs `Date.parse`
289
+ before the duration fallback — and recurses with the other's parser off, so a
290
+ value cannot bounce between them.
291
+
292
+ ```ts
293
+ object.toDate("30 days ago", { parseDuration: false }); // undefined
294
+ object.toDuration("2026-08-02", { parseDate: false }); // undefined
295
+ ```
296
+
297
+ Turn the fallback off where only one reading can be correct. A stored timestamp
298
+ or an `expires_at` field must not accept a duration, because resolving against
299
+ the current clock makes the same input mean something different on every call. A
300
+ timeout or a poll interval must not accept a date, because it would yield a
301
+ plausible but wrong number that also drifts.
302
+
241
303
  ## Iterables
242
304
 
243
305
  ```ts
@@ -369,9 +431,10 @@ without paying formatting cost when disabled.
369
431
  - `json` - non-throwing `parse()` and record-narrowing `parseRecord()`.
370
432
  - `string` - tokenization, slugs, identifiers, human labels, string coercion,
371
433
  config lists, descriptions, pluralization, and HTML escaping.
372
- - `object` - record checks, boolean/date/duration coercion, present-only field
373
- spreading, deep equality, shape types, and lazy sequence transforms +
374
- collection helpers.
434
+ - `object` - record checks, number/boolean/date/duration coercion, present-only
435
+ field spreading, deep equality, JSON-round-trip guards
436
+ (`isSerializableValue`), canonical identity keys (`toStableKey`), shape types,
437
+ and lazy sequence transforms + collection helpers.
375
438
  - `env` - config-over-environment resolution: strings, booleans, positive
376
439
  numbers/integers, and lists, with env-name fallback chains. `name()` gives the
377
440
  primary variable name for a log line or error - an `EnvKey` may be a bare
package/index.ts CHANGED
@@ -28,7 +28,7 @@ export type { MemoizeOptions } from "./src/function.ts";
28
28
  export type { HeaderLike } from "./src/http.ts";
29
29
  export type { LogLevel, Logger } from "./src/log.ts";
30
30
  export type { UrlLike, IpVersion, ParsedIp, Cidr, UrlBuilder, ParseEmailsOptions } from "./src/net.ts";
31
- export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, DeepEqualComparator } from "./src/object.ts";
31
+ export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, SerializablePrimitive, SerializableValue, ToNumberOptions, ToDateOptions, ToDurationOptions, DeepEqualComparator } from "./src/object.ts";
32
32
  export type { PatternOptions } from "./src/pattern.ts";
33
33
  export type { PredicateFunction, TypePredicateFunction, PredicateInput, Predicate } from "./src/predicate.ts";
34
34
  export type { TokenizeOptions, KeyOptions, IdentifierOptions, Description } from "./src/string.ts";
package/lib/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export type { MemoizeOptions } from "./src/function.ts";
24
24
  export type { HeaderLike } from "./src/http.ts";
25
25
  export type { LogLevel, Logger } from "./src/log.ts";
26
26
  export type { UrlLike, IpVersion, ParsedIp, Cidr, UrlBuilder, ParseEmailsOptions } from "./src/net.ts";
27
- export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, DeepEqualComparator } from "./src/object.ts";
27
+ export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, SerializablePrimitive, SerializableValue, ToNumberOptions, ToDateOptions, ToDurationOptions, DeepEqualComparator } from "./src/object.ts";
28
28
  export type { PatternOptions } from "./src/pattern.ts";
29
29
  export type { PredicateFunction, TypePredicateFunction, PredicateInput, Predicate } from "./src/predicate.ts";
30
30
  export type { TokenizeOptions, KeyOptions, IdentifierOptions, Description } from "./src/string.ts";
package/lib/index.js CHANGED
@@ -19,4 +19,4 @@ export * as string from "./src/string.js";
19
19
  export * as token from "./src/token.js";
20
20
  export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand.js";
21
21
  export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER } from "./src/token.js";
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxjQUFjLE1BQU0sbUJBQW1CLENBQUM7QUFDcEQsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxTQUFTLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBRXhDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFBRSxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBYXpKLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGFzeW5jIGZyb20gXCIuL3NyYy9hc3luYy50c1wiO1xuZXhwb3J0ICogYXMgYnJhbmQgZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgKiBhcyBlbnYgZnJvbSBcIi4vc3JjL2Vudi50c1wiO1xuZXhwb3J0ICogYXMgZXJyb3IgZnJvbSBcIi4vc3JjL2Vycm9yLnRzXCI7XG5leHBvcnQgKiBhcyBleGVjdXRpb24gZnJvbSBcIi4vc3JjL2V4ZWN1dGlvbi50c1wiO1xuZXhwb3J0ICogYXMgZnVuY3Rpb25Nb2R1bGUgZnJvbSBcIi4vc3JjL2Z1bmN0aW9uLnRzXCI7XG5leHBvcnQgKiBhcyBoYXNoIGZyb20gXCIuL3NyYy9oYXNoLnRzXCI7XG5leHBvcnQgKiBhcyBodHRwIGZyb20gXCIuL3NyYy9odHRwLnRzXCI7XG5leHBvcnQgKiBhcyBqc29uIGZyb20gXCIuL3NyYy9qc29uLnRzXCI7XG5leHBvcnQgKiBhcyBsb2cgZnJvbSBcIi4vc3JjL2xvZy50c1wiO1xuZXhwb3J0ICogYXMgbmV0IGZyb20gXCIuL3NyYy9uZXQudHNcIjtcbmV4cG9ydCAqIGFzIG9iamVjdCBmcm9tIFwiLi9zcmMvb2JqZWN0LnRzXCI7XG5leHBvcnQgKiBhcyBwYXR0ZXJuIGZyb20gXCIuL3NyYy9wYXR0ZXJuLnRzXCI7XG5leHBvcnQgKiBhcyBwcmVkaWNhdGUgZnJvbSBcIi4vc3JjL3ByZWRpY2F0ZS50c1wiO1xuZXhwb3J0ICogYXMgc3RyaW5nIGZyb20gXCIuL3NyYy9zdHJpbmcudHNcIjtcbmV4cG9ydCAqIGFzIHRva2VuIGZyb20gXCIuL3NyYy90b2tlbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQb2xsQ29udGV4dCwgUG9sbFByb2R1Y2VyLCBQb2xsT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9hc3luYy50c1wiO1xuZXhwb3J0IHsgREVGQVVMVF9CUkFORF9BU1NFVFMsIEJyYW5kQXNzZXRTZXRTY2hlbWEsIEJyYW5kQ29sb3JzU2NoZW1hLCBCcmFuZFZvaWNlU2NoZW1hLCBCcmFuZENvbnRleHRTY2hlbWEsIGRlZmF1bHRCcmFuZENvbnRleHQgfSBmcm9tIFwiLi9zcmMvYnJhbmQudHNcIjtcbmV4cG9ydCB0eXBlIHsgQnJhbmRDb250ZXh0LCBCcmFuZENvbnRleHRJbnB1dCwgQnJhbmRBc3NldFNldCB9IGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0IHR5cGUgeyBFbnZLZXkgfSBmcm9tIFwiLi9zcmMvZW52LnRzXCI7XG5leHBvcnQgdHlwZSB7IEVycm9yQ29udGV4dCB9IGZyb20gXCIuL3NyYy9lcnJvci50c1wiO1xuZXhwb3J0IHR5cGUgeyBFeGVjdXRpb25SZXN1bHQsIEV4ZWN1dG9yLCBFeGVjdXRpb25GYWlsdXJlLCBSdW5PcHRpb25zIH0gZnJvbSBcIi4vc3JjL2V4ZWN1dGlvbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBNZW1vaXplT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9mdW5jdGlvbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBIZWFkZXJMaWtlIH0gZnJvbSBcIi4vc3JjL2h0dHAudHNcIjtcbmV4cG9ydCB0eXBlIHsgTG9nTGV2ZWwsIExvZ2dlciB9IGZyb20gXCIuL3NyYy9sb2cudHNcIjtcbmV4cG9ydCB0eXBlIHsgVXJsTGlrZSwgSXBWZXJzaW9uLCBQYXJzZWRJcCwgQ2lkciwgVXJsQnVpbGRlciwgUGFyc2VFbWFpbHNPcHRpb25zIH0gZnJvbSBcIi4vc3JjL25ldC50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZXF1ZW5jZSwgQ29udGFpbmVyLCBDb2xsZWN0aW9uLCBPbmVPck1hbnksIE5hbWVMaWtlLCBOb25GdW5jdGlvbktleXMsIERlZXBFcXVhbENvbXBhcmF0b3IgfSBmcm9tIFwiLi9zcmMvb2JqZWN0LnRzXCI7XG5leHBvcnQgdHlwZSB7IFBhdHRlcm5PcHRpb25zIH0gZnJvbSBcIi4vc3JjL3BhdHRlcm4udHNcIjtcbmV4cG9ydCB0eXBlIHsgUHJlZGljYXRlRnVuY3Rpb24sIFR5cGVQcmVkaWNhdGVGdW5jdGlvbiwgUHJlZGljYXRlSW5wdXQsIFByZWRpY2F0ZSB9IGZyb20gXCIuL3NyYy9wcmVkaWNhdGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgVG9rZW5pemVPcHRpb25zLCBLZXlPcHRpb25zLCBJZGVudGlmaWVyT3B0aW9ucywgRGVzY3JpcHRpb24gfSBmcm9tIFwiLi9zcmMvc3RyaW5nLnRzXCI7XG5leHBvcnQgeyBBQ0NFU1NfVE9LRU5fSEVBREVSLCBVU0VSX0lEX0hFQURFUiwgVVNFUl9FTUFJTF9IRUFERVIgfSBmcm9tIFwiLi9zcmMvdG9rZW4udHNcIjtcbiJdfQ==
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxjQUFjLE1BQU0sbUJBQW1CLENBQUM7QUFDcEQsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxTQUFTLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBRXhDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFBRSxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBYXpKLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGFzeW5jIGZyb20gXCIuL3NyYy9hc3luYy50c1wiO1xuZXhwb3J0ICogYXMgYnJhbmQgZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgKiBhcyBlbnYgZnJvbSBcIi4vc3JjL2Vudi50c1wiO1xuZXhwb3J0ICogYXMgZXJyb3IgZnJvbSBcIi4vc3JjL2Vycm9yLnRzXCI7XG5leHBvcnQgKiBhcyBleGVjdXRpb24gZnJvbSBcIi4vc3JjL2V4ZWN1dGlvbi50c1wiO1xuZXhwb3J0ICogYXMgZnVuY3Rpb25Nb2R1bGUgZnJvbSBcIi4vc3JjL2Z1bmN0aW9uLnRzXCI7XG5leHBvcnQgKiBhcyBoYXNoIGZyb20gXCIuL3NyYy9oYXNoLnRzXCI7XG5leHBvcnQgKiBhcyBodHRwIGZyb20gXCIuL3NyYy9odHRwLnRzXCI7XG5leHBvcnQgKiBhcyBqc29uIGZyb20gXCIuL3NyYy9qc29uLnRzXCI7XG5leHBvcnQgKiBhcyBsb2cgZnJvbSBcIi4vc3JjL2xvZy50c1wiO1xuZXhwb3J0ICogYXMgbmV0IGZyb20gXCIuL3NyYy9uZXQudHNcIjtcbmV4cG9ydCAqIGFzIG9iamVjdCBmcm9tIFwiLi9zcmMvb2JqZWN0LnRzXCI7XG5leHBvcnQgKiBhcyBwYXR0ZXJuIGZyb20gXCIuL3NyYy9wYXR0ZXJuLnRzXCI7XG5leHBvcnQgKiBhcyBwcmVkaWNhdGUgZnJvbSBcIi4vc3JjL3ByZWRpY2F0ZS50c1wiO1xuZXhwb3J0ICogYXMgc3RyaW5nIGZyb20gXCIuL3NyYy9zdHJpbmcudHNcIjtcbmV4cG9ydCAqIGFzIHRva2VuIGZyb20gXCIuL3NyYy90b2tlbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQb2xsQ29udGV4dCwgUG9sbFByb2R1Y2VyLCBQb2xsT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9hc3luYy50c1wiO1xuZXhwb3J0IHsgREVGQVVMVF9CUkFORF9BU1NFVFMsIEJyYW5kQXNzZXRTZXRTY2hlbWEsIEJyYW5kQ29sb3JzU2NoZW1hLCBCcmFuZFZvaWNlU2NoZW1hLCBCcmFuZENvbnRleHRTY2hlbWEsIGRlZmF1bHRCcmFuZENvbnRleHQgfSBmcm9tIFwiLi9zcmMvYnJhbmQudHNcIjtcbmV4cG9ydCB0eXBlIHsgQnJhbmRDb250ZXh0LCBCcmFuZENvbnRleHRJbnB1dCwgQnJhbmRBc3NldFNldCB9IGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0IHR5cGUgeyBFbnZLZXkgfSBmcm9tIFwiLi9zcmMvZW52LnRzXCI7XG5leHBvcnQgdHlwZSB7IEVycm9yQ29udGV4dCB9IGZyb20gXCIuL3NyYy9lcnJvci50c1wiO1xuZXhwb3J0IHR5cGUgeyBFeGVjdXRpb25SZXN1bHQsIEV4ZWN1dG9yLCBFeGVjdXRpb25GYWlsdXJlLCBSdW5PcHRpb25zIH0gZnJvbSBcIi4vc3JjL2V4ZWN1dGlvbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBNZW1vaXplT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9mdW5jdGlvbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBIZWFkZXJMaWtlIH0gZnJvbSBcIi4vc3JjL2h0dHAudHNcIjtcbmV4cG9ydCB0eXBlIHsgTG9nTGV2ZWwsIExvZ2dlciB9IGZyb20gXCIuL3NyYy9sb2cudHNcIjtcbmV4cG9ydCB0eXBlIHsgVXJsTGlrZSwgSXBWZXJzaW9uLCBQYXJzZWRJcCwgQ2lkciwgVXJsQnVpbGRlciwgUGFyc2VFbWFpbHNPcHRpb25zIH0gZnJvbSBcIi4vc3JjL25ldC50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZXF1ZW5jZSwgQ29udGFpbmVyLCBDb2xsZWN0aW9uLCBPbmVPck1hbnksIE5hbWVMaWtlLCBOb25GdW5jdGlvbktleXMsIFNlcmlhbGl6YWJsZVByaW1pdGl2ZSwgU2VyaWFsaXphYmxlVmFsdWUsIFRvTnVtYmVyT3B0aW9ucywgVG9EYXRlT3B0aW9ucywgVG9EdXJhdGlvbk9wdGlvbnMsIERlZXBFcXVhbENvbXBhcmF0b3IgfSBmcm9tIFwiLi9zcmMvb2JqZWN0LnRzXCI7XG5leHBvcnQgdHlwZSB7IFBhdHRlcm5PcHRpb25zIH0gZnJvbSBcIi4vc3JjL3BhdHRlcm4udHNcIjtcbmV4cG9ydCB0eXBlIHsgUHJlZGljYXRlRnVuY3Rpb24sIFR5cGVQcmVkaWNhdGVGdW5jdGlvbiwgUHJlZGljYXRlSW5wdXQsIFByZWRpY2F0ZSB9IGZyb20gXCIuL3NyYy9wcmVkaWNhdGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgVG9rZW5pemVPcHRpb25zLCBLZXlPcHRpb25zLCBJZGVudGlmaWVyT3B0aW9ucywgRGVzY3JpcHRpb24gfSBmcm9tIFwiLi9zcmMvc3RyaW5nLnRzXCI7XG5leHBvcnQgeyBBQ0NFU1NfVE9LRU5fSEVBREVSLCBVU0VSX0lEX0hFQURFUiwgVVNFUl9FTUFJTF9IRUFERVIgfSBmcm9tIFwiLi9zcmMvdG9rZW4udHNcIjtcbiJdfQ==
package/lib/src/env.js CHANGED
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * @module
16
16
  */
17
- import { toBoolean } from "./object.js";
17
+ import { toBoolean, toNumber } from "./object.js";
18
18
  import { parseList, trimToNull } from "./string.js";
19
19
  /** The ambient environment, or `{}` off-process (a browser). */
20
20
  function environment() {
@@ -104,10 +104,8 @@ export function positiveInt(configured, keys, fallback) {
104
104
  return toPositiveInt(configured) ?? toPositiveInt(text(keys)) ?? fallback;
105
105
  }
106
106
  function toPositiveNumber(value) {
107
- if (value === null || value === undefined || value === "")
108
- return undefined;
109
- const parsed = Number(value);
110
- return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
107
+ const parsed = toNumber(value);
108
+ return parsed !== undefined && parsed > 0 ? parsed : undefined;
111
109
  }
112
110
  function toPositiveInt(value) {
113
111
  const parsed = toPositiveNumber(value);
@@ -125,4 +123,4 @@ export function list(configured, keys, transform) {
125
123
  const fromConfig = parseList(configured, transform);
126
124
  return fromConfig.length > 0 ? fromConfig : parseList(text(keys), transform);
127
125
  }
128
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2Vudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBVXBELGdFQUFnRTtBQUNoRSxTQUFTLFdBQVc7SUFDbEIsT0FBUSxVQUF3QyxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDO0FBQ3RFLENBQUM7QUFFRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxVQUFVLElBQUksQ0FBQyxJQUFZO0lBQy9CLE9BQU8sT0FBTyxJQUFJLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBQzNELENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxJQUFJLENBQUMsSUFBWTtJQUMvQixNQUFNLEdBQUcsR0FBRyxXQUFXLEVBQUUsQ0FBQztJQUMxQixLQUFLLE1BQU0sR0FBRyxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDM0QsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25DLElBQUksS0FBSyxLQUFLLElBQUk7WUFBRSxPQUFPLEtBQUssQ0FBQztJQUNuQyxDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLE1BQU0sQ0FBQyxVQUFtQixFQUFFLElBQVk7SUFDdEQsT0FBTyxVQUFVLENBQUMsVUFBVSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQzlDLENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxPQUFPLENBQUMsVUFBbUIsRUFBRSxJQUFZO0lBQ3ZELE9BQU8sU0FBUyxDQUFDLFVBQVUsQ0FBQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztBQUN4RCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQUMsVUFBbUIsRUFBRSxJQUFZLEVBQUUsUUFBZ0I7SUFDaEYsT0FBTyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUMsSUFBSSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxRQUFRLENBQUM7QUFDbEYsQ0FBQztBQUVEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxVQUFtQixFQUFFLElBQVksRUFBRSxRQUFnQjtJQUM3RSxPQUFPLGFBQWEsQ0FBQyxVQUFVLENBQUMsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksUUFBUSxDQUFDO0FBQzVFLENBQUM7QUFFRCxTQUFTLGdCQUFnQixDQUFDLEtBQWM7SUFDdEMsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1RSxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDN0IsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ3BFLENBQUM7QUFFRCxTQUFTLGFBQWEsQ0FBQyxLQUFjO0lBQ25DLE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3ZDLE9BQU8sTUFBTSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBQy9ELENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLElBQUksQ0FDbEIsVUFBeUQsRUFDekQsSUFBWSxFQUNaLFNBQXFDO0lBRXJDLE1BQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDcEQsT0FBTyxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQy9FLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlYWRpbmcgY29uZmlndXJhdGlvbiBvdXQgb2YgdGhlIGVudmlyb25tZW50LlxuICpcbiAqIEV2ZXJ5IHBsdWdpbiBjb25maWcgaW4gdGhpcyByZXBvIHJlc29sdmVzIHRoZSBzYW1lIHdheTogdGFrZSB0aGUgdHlwZWRcbiAqIGNvbmZpZyB2YWx1ZSB3aGVuIHRoZSBjYWxsZXIgc2V0IG9uZSwgZWxzZSBmYWxsIGJhY2sgdG8gb25lIG9yIG1vcmVcbiAqIGVudmlyb25tZW50IHZhcmlhYmxlcywgZWxzZSBhIGRlZmF1bHQuIFdyaXR0ZW4gYnkgaGFuZCB0aGF0IGJlY29tZXMgYVxuICogYGNvbmZpZy54ID8/IE51bWJlcihwcm9jZXNzLmVudi5YKWAgY2hhaW4gcGVyIGZpZWxkLCBhbmQgZWFjaCBwYWNrYWdlIGdyZXdcbiAqIGl0cyBvd24gYGZyb21FbnZgIC8gYHJlc29sdmVQb3NpdGl2ZUludGAgaGVscGVyIHdpdGggc2xpZ2h0bHkgZGlmZmVyZW50XG4gKiBjb2VyY2lvbiBydWxlcy4gVGhlc2UgYXJlIHRob3NlIGhlbHBlcnMsIG9uY2UuXG4gKlxuICogQnJvd3Nlci1zYWZlOiBgcHJvY2Vzc2AgaXMgcmVhY2hlZCB0aHJvdWdoIGBnbG9iYWxUaGlzYCBhbmQgZ3VhcmRlZCwgc28gdGhpc1xuICogbW9kdWxlIG5lZWRzIG5vIE5vZGUgdHlwZXMgYW5kIGlzIGluZXJ0IGluIGEgYnJvd3NlciAoZXZlcnkgbG9va3VwIG1pc3NlcyBhbmRcbiAqIHRoZSBjYWxsZXIncyBmYWxsYmFjayBhcHBsaWVzKS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgdG9Cb29sZWFuIH0gZnJvbSBcIi4vb2JqZWN0LnRzXCI7XG5pbXBvcnQgeyBwYXJzZUxpc3QsIHRyaW1Ub051bGwgfSBmcm9tIFwiLi9zdHJpbmcudHNcIjtcblxuLyoqIGBwcm9jZXNzYC1zaGFwZWQgdmlldyBvZmYgYGdsb2JhbFRoaXNgLCBzbyBubyBub2RlIHR5cGVzIGFyZSBuZWVkZWQuICovXG5pbnRlcmZhY2UgUHJvY2Vzc0xpa2Uge1xuICBlbnY/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCB1bmRlZmluZWQ+O1xufVxuXG4vKiogT25lIGVudiB2YXIgbmFtZSwgb3Igc2V2ZXJhbCB0cmllZCBpbiBvcmRlci4gKi9cbmV4cG9ydCB0eXBlIEVudktleSA9IHN0cmluZyB8IHJlYWRvbmx5IHN0cmluZ1tdO1xuXG4vKiogVGhlIGFtYmllbnQgZW52aXJvbm1lbnQsIG9yIGB7fWAgb2ZmLXByb2Nlc3MgKGEgYnJvd3NlcikuICovXG5mdW5jdGlvbiBlbnZpcm9ubWVudCgpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgcmV0dXJuIChnbG9iYWxUaGlzIGFzIHsgcHJvY2Vzcz86IFByb2Nlc3NMaWtlIH0pLnByb2Nlc3M/LmVudiA/PyB7fTtcbn1cblxuLyoqXG4gKiBUaGUgUFJJTUFSWSAoY3VycmVudCwgbm9uLWRlcHJlY2F0ZWQpIG5hbWUgaW4gYW4ge0BsaW5rIEVudktleX0uXG4gKlxuICogVXNlIHRoaXMgd2hlbiBuYW1pbmcgYSB2YXJpYWJsZSBpbiBhIGxvZyBsaW5lIG9yIGVycm9yIHJhdGhlciB0aGFuIGluZGV4aW5nXG4gKiBga2V5c1swXWA6IGFuIHtAbGluayBFbnZLZXl9IG1heSBiZSBhIGJhcmUgc3RyaW5nLCBhbmQgYFwiVFVOTkVMX1hcIlswXWAgaXMgdGhlXG4gKiBjaGFyYWN0ZXIgYFwiVFwiYCwgd2hpY2ggcHJvZHVjZXMgYSBtZXNzYWdlIG5hbWluZyBhIHZhcmlhYmxlIHRoYXQgZG9lcyBub3RcbiAqIGV4aXN0LiBSZXR1cm5zIGBcIlwiYCBvbmx5IGZvciBhbiBlbXB0eSBsaXN0LCB3aGljaCBubyBjYWxsZXIgc2hvdWxkIGhhdmUuXG4gKlxuICogQGV4YW1wbGVcbiAqIGxvZ2dlci53YXJuKGAke2Vudi5uYW1lKEpXVF9TRUNSRVRfRU5WKX0gaXMgbm90IHNldGApO1xuICovXG5leHBvcnQgZnVuY3Rpb24gbmFtZShrZXlzOiBFbnZLZXkpOiBzdHJpbmcge1xuICByZXR1cm4gdHlwZW9mIGtleXMgPT09IFwic3RyaW5nXCIgPyBrZXlzIDogKGtleXNbMF0gPz8gXCJcIik7XG59XG5cbi8qKlxuICogRmlyc3Qgbm9uLWVtcHR5IHZhbHVlIGFtb25nIGBrZXlzYCwgdHJpbW1lZCwgZWxzZSBgbnVsbGAuXG4gKlxuICogU2V2ZXJhbCBuYW1lcyBmb3Igb25lIHNldHRpbmcgaXMgdGhlIG5vcm0gKGEgcGFja2FnZS1zcGVjaWZpYyB2YXJpYWJsZSBwbHVzXG4gKiB0aGUgRGF0YWJyaWNrcy1zdGFuZGFyZCBvbmUpLCBzbyBga2V5c2AgaXMgb3JkZXItc2Vuc2l0aXZlOiBlYXJsaWVyIG5hbWVzIHdpbi5cbiAqXG4gKiBAZXhhbXBsZVxuICogZW52LnRleHQoW1wiVEVBTVNfQVBQX0lEXCIsIFwiTUlDUk9TT0ZUX0FQUF9JRFwiXSk7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0ZXh0KGtleXM6IEVudktleSk6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBlbnYgPSBlbnZpcm9ubWVudCgpO1xuICBmb3IgKGNvbnN0IGtleSBvZiB0eXBlb2Yga2V5cyA9PT0gXCJzdHJpbmdcIiA/IFtrZXlzXSA6IGtleXMpIHtcbiAgICBjb25zdCB2YWx1ZSA9IHRyaW1Ub051bGwoZW52W2tleV0pO1xuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCkgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSBzdHJpbmcgc2V0dGluZzogYGNvbmZpZ3VyZWRgIHdoZW4gc2V0IGFuZCBub24tZW1wdHksIGVsc2UgdGhlIGZpcnN0XG4gKiBub24tZW1wdHkgdmFyaWFibGUgYW1vbmcgYGtleXNgLCBlbHNlIGBudWxsYC5cbiAqXG4gKiBAZXhhbXBsZVxuICogZW52LnN0cmluZyhjb25maWcuaG9zdCwgXCJTTVRQX0hPU1RcIik7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdHJpbmcoY29uZmlndXJlZDogdW5rbm93biwga2V5czogRW52S2V5KTogc3RyaW5nIHwgbnVsbCB7XG4gIHJldHVybiB0cmltVG9OdWxsKGNvbmZpZ3VyZWQpID8/IHRleHQoa2V5cyk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIGJvb2xlYW4gc2V0dGluZyB0aHJvdWdoIHtAbGluayB0b0Jvb2xlYW59LCBzbyB0aGUgbG9vc2Ugc3BlbGxpbmdzIGFuXG4gKiBlbnYgdmFyIGFjdHVhbGx5IGNhcnJpZXMgKGAxYCwgYG9uYCwgYHllc2AsIC4uLikgYXJlIGFjY2VwdGVkLiBSZXR1cm5zXG4gKiBgdW5kZWZpbmVkYCB3aGVuIG5laXRoZXIgc291cmNlIGlzIGludGVycHJldGFibGUsIGxldHRpbmcgdGhlIGNhbGxlciBwaWNrIGFcbiAqIGRlZmF1bHQgd2l0aCBgPz9gLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYuYm9vbGVhbihjb25maWcuZnV6enksIFwiV0VCX1NFQVJDSF9GVVpaWVwiKSA/PyB0cnVlO1xuICovXG5leHBvcnQgZnVuY3Rpb24gYm9vbGVhbihjb25maWd1cmVkOiB1bmtub3duLCBrZXlzOiBFbnZLZXkpOiBib29sZWFuIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHRvQm9vbGVhbihjb25maWd1cmVkKSA/PyB0b0Jvb2xlYW4odGV4dChrZXlzKSk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIHBvc2l0aXZlLW51bWJlciBzZXR0aW5nIHRoYXQgbWF5IGJlIGZyYWN0aW9uYWwgKGEgc2NvcmUgdGhyZXNob2xkLFxuICogYSByYXRpbyk6IGBjb25maWd1cmVkYCB3aGVuIGl0IGlzIGEgZmluaXRlIG51bWJlciBncmVhdGVyIHRoYW4gemVybywgZWxzZSB0aGVcbiAqIGZpcnN0IHZhcmlhYmxlIGFtb25nIGBrZXlzYCB0aGF0IHBhcnNlcyB0aGF0IHdheSwgZWxzZSBgZmFsbGJhY2tgLlxuICpcbiAqIHtAbGluayBwb3NpdGl2ZUludH0gaXMgdGhlIHJpZ2h0IGNob2ljZSBmb3IgYSBjb3VudDsgdGhpcyBvbmUga2VlcHMgdGhlXG4gKiBmcmFjdGlvbiwgc28gYSBgMC40YCB0aHJlc2hvbGQgZG9lcyBub3QgZmxvb3IgdG8gYDBgLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYucG9zaXRpdmVOdW1iZXIoY29uZmlnLmZ1enp5VGhyZXNob2xkLCBcIlNFQVJDSF9GVVpaWV9USFJFU0hPTERcIiwgMC40KTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvc2l0aXZlTnVtYmVyKGNvbmZpZ3VyZWQ6IHVua25vd24sIGtleXM6IEVudktleSwgZmFsbGJhY2s6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiB0b1Bvc2l0aXZlTnVtYmVyKGNvbmZpZ3VyZWQpID8/IHRvUG9zaXRpdmVOdW1iZXIodGV4dChrZXlzKSkgPz8gZmFsbGJhY2s7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIHBvc2l0aXZlLWludGVnZXIgc2V0dGluZyAoYSBwb3J0LCB0aW1lb3V0LCBwYWdlIHNpemUsIG9yIGNhcCk6XG4gKiBgY29uZmlndXJlZGAgd2hlbiBpdCBpcyBhIGZpbml0ZSBudW1iZXIgZ3JlYXRlciB0aGFuIHplcm8sIGVsc2UgdGhlIGZpcnN0XG4gKiB2YXJpYWJsZSBhbW9uZyBga2V5c2AgdGhhdCBwYXJzZXMgdGhhdCB3YXksIGVsc2UgYGZhbGxiYWNrYC4gRmxvb3JlZCwgc28gYVxuICogZnJhY3Rpb25hbCB2YWx1ZSBjYW4ndCBsZWFrIGludG8gYSBjb3VudC5cbiAqXG4gKiBBIG5vbi1udW1lcmljIG9yIG5vbi1wb3NpdGl2ZSB2YWx1ZSBpcyB0cmVhdGVkIGFzIGFic2VudCByYXRoZXIgdGhhbiBmYXRhbCAtXG4gKiB0aGVzZSBhcmUgY2VpbGluZ3MgYW5kIHRpbWVvdXRzIHdoZXJlIGEgc2FuZSBkZWZhdWx0IGJlYXRzIGEgYm9vdCBmYWlsdXJlLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYucG9zaXRpdmVJbnQoY29uZmlnLnRpbWVvdXRNcywgXCJTRUFSQ0hfVElNRU9VVF9NU1wiLCAzMF8wMDApO1xuICovXG5leHBvcnQgZnVuY3Rpb24gcG9zaXRpdmVJbnQoY29uZmlndXJlZDogdW5rbm93biwga2V5czogRW52S2V5LCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHRvUG9zaXRpdmVJbnQoY29uZmlndXJlZCkgPz8gdG9Qb3NpdGl2ZUludCh0ZXh0KGtleXMpKSA/PyBmYWxsYmFjaztcbn1cblxuZnVuY3Rpb24gdG9Qb3NpdGl2ZU51bWJlcih2YWx1ZTogdW5rbm93bik6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gIGlmICh2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBcIlwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBwYXJzZWQgPSBOdW1iZXIodmFsdWUpO1xuICByZXR1cm4gTnVtYmVyLmlzRmluaXRlKHBhcnNlZCkgJiYgcGFyc2VkID4gMCA/IHBhcnNlZCA6IHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gdG9Qb3NpdGl2ZUludCh2YWx1ZTogdW5rbm93bik6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHBhcnNlZCA9IHRvUG9zaXRpdmVOdW1iZXIodmFsdWUpO1xuICByZXR1cm4gcGFyc2VkID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiBNYXRoLmZsb29yKHBhcnNlZCk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIGxpc3Qgc2V0dGluZyB0aHJvdWdoIHtAbGluayBwYXJzZUxpc3R9LCBzbyBhbiBhcnJheSBmcm9tIHR5cGVkXG4gKiBjb25maWcgYW5kIGEgYFwiYSwgYiBjXCJgIGVudiBzdHJpbmcgbm9ybWFsaXplIGlkZW50aWNhbGx5LiBSZXR1cm5zIGBbXWAgd2hlblxuICogbmVpdGhlciBzb3VyY2UgaGFzIGVudHJpZXMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGVudi5saXN0KGNvbmZpZy5tb2RlbEZhbGxiYWNrcywgXCJXRUJfU0VBUkNIX01PREVMX0ZBTExCQUNLU1wiKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpc3QoXG4gIGNvbmZpZ3VyZWQ6IHN0cmluZyB8IHJlYWRvbmx5IHN0cmluZ1tdIHwgdW5kZWZpbmVkIHwgbnVsbCxcbiAga2V5czogRW52S2V5LFxuICB0cmFuc2Zvcm0/OiAoZW50cnk6IHN0cmluZykgPT4gc3RyaW5nLFxuKTogc3RyaW5nW10ge1xuICBjb25zdCBmcm9tQ29uZmlnID0gcGFyc2VMaXN0KGNvbmZpZ3VyZWQsIHRyYW5zZm9ybSk7XG4gIHJldHVybiBmcm9tQ29uZmlnLmxlbmd0aCA+IDAgPyBmcm9tQ29uZmlnIDogcGFyc2VMaXN0KHRleHQoa2V5cyksIHRyYW5zZm9ybSk7XG59XG4iXX0=
126
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2Vudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQVVwRCxnRUFBZ0U7QUFDaEUsU0FBUyxXQUFXO0lBQ2xCLE9BQVEsVUFBd0MsQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQztBQUN0RSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSxJQUFJLENBQUMsSUFBWTtJQUMvQixPQUFPLE9BQU8sSUFBSSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUMzRCxDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsSUFBSSxDQUFDLElBQVk7SUFDL0IsTUFBTSxHQUFHLEdBQUcsV0FBVyxFQUFFLENBQUM7SUFDMUIsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzNELE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNuQyxJQUFJLEtBQUssS0FBSyxJQUFJO1lBQUUsT0FBTyxLQUFLLENBQUM7SUFDbkMsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxNQUFNLENBQUMsVUFBbUIsRUFBRSxJQUFZO0lBQ3RELE9BQU8sVUFBVSxDQUFDLFVBQVUsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM5QyxDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsT0FBTyxDQUFDLFVBQW1CLEVBQUUsSUFBWTtJQUN2RCxPQUFPLFNBQVMsQ0FBQyxVQUFVLENBQUMsSUFBSSxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDeEQsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUFDLFVBQW1CLEVBQUUsSUFBWSxFQUFFLFFBQWdCO0lBQ2hGLE9BQU8sZ0JBQWdCLENBQUMsVUFBVSxDQUFDLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksUUFBUSxDQUFDO0FBQ2xGLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7R0FXRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsVUFBbUIsRUFBRSxJQUFZLEVBQUUsUUFBZ0I7SUFDN0UsT0FBTyxhQUFhLENBQUMsVUFBVSxDQUFDLElBQUksYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLFFBQVEsQ0FBQztBQUM1RSxDQUFDO0FBRUQsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFjO0lBQ3RDLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMvQixPQUFPLE1BQU0sS0FBSyxTQUFTLElBQUksTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDakUsQ0FBQztBQUVELFNBQVMsYUFBYSxDQUFDLEtBQWM7SUFDbkMsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDdkMsT0FBTyxNQUFNLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDL0QsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsSUFBSSxDQUNsQixVQUF5RCxFQUN6RCxJQUFZLEVBQ1osU0FBcUM7SUFFckMsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDLFVBQVUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNwRCxPQUFPLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7QUFDL0UsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVhZGluZyBjb25maWd1cmF0aW9uIG91dCBvZiB0aGUgZW52aXJvbm1lbnQuXG4gKlxuICogRXZlcnkgcGx1Z2luIGNvbmZpZyBpbiB0aGlzIHJlcG8gcmVzb2x2ZXMgdGhlIHNhbWUgd2F5OiB0YWtlIHRoZSB0eXBlZFxuICogY29uZmlnIHZhbHVlIHdoZW4gdGhlIGNhbGxlciBzZXQgb25lLCBlbHNlIGZhbGwgYmFjayB0byBvbmUgb3IgbW9yZVxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCBlbHNlIGEgZGVmYXVsdC4gV3JpdHRlbiBieSBoYW5kIHRoYXQgYmVjb21lcyBhXG4gKiBgY29uZmlnLnggPz8gTnVtYmVyKHByb2Nlc3MuZW52LlgpYCBjaGFpbiBwZXIgZmllbGQsIGFuZCBlYWNoIHBhY2thZ2UgZ3Jld1xuICogaXRzIG93biBgZnJvbUVudmAgLyBgcmVzb2x2ZVBvc2l0aXZlSW50YCBoZWxwZXIgd2l0aCBzbGlnaHRseSBkaWZmZXJlbnRcbiAqIGNvZXJjaW9uIHJ1bGVzLiBUaGVzZSBhcmUgdGhvc2UgaGVscGVycywgb25jZS5cbiAqXG4gKiBCcm93c2VyLXNhZmU6IGBwcm9jZXNzYCBpcyByZWFjaGVkIHRocm91Z2ggYGdsb2JhbFRoaXNgIGFuZCBndWFyZGVkLCBzbyB0aGlzXG4gKiBtb2R1bGUgbmVlZHMgbm8gTm9kZSB0eXBlcyBhbmQgaXMgaW5lcnQgaW4gYSBicm93c2VyIChldmVyeSBsb29rdXAgbWlzc2VzIGFuZFxuICogdGhlIGNhbGxlcidzIGZhbGxiYWNrIGFwcGxpZXMpLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyB0b0Jvb2xlYW4sIHRvTnVtYmVyIH0gZnJvbSBcIi4vb2JqZWN0LnRzXCI7XG5pbXBvcnQgeyBwYXJzZUxpc3QsIHRyaW1Ub051bGwgfSBmcm9tIFwiLi9zdHJpbmcudHNcIjtcblxuLyoqIGBwcm9jZXNzYC1zaGFwZWQgdmlldyBvZmYgYGdsb2JhbFRoaXNgLCBzbyBubyBub2RlIHR5cGVzIGFyZSBuZWVkZWQuICovXG5pbnRlcmZhY2UgUHJvY2Vzc0xpa2Uge1xuICBlbnY/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCB1bmRlZmluZWQ+O1xufVxuXG4vKiogT25lIGVudiB2YXIgbmFtZSwgb3Igc2V2ZXJhbCB0cmllZCBpbiBvcmRlci4gKi9cbmV4cG9ydCB0eXBlIEVudktleSA9IHN0cmluZyB8IHJlYWRvbmx5IHN0cmluZ1tdO1xuXG4vKiogVGhlIGFtYmllbnQgZW52aXJvbm1lbnQsIG9yIGB7fWAgb2ZmLXByb2Nlc3MgKGEgYnJvd3NlcikuICovXG5mdW5jdGlvbiBlbnZpcm9ubWVudCgpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgcmV0dXJuIChnbG9iYWxUaGlzIGFzIHsgcHJvY2Vzcz86IFByb2Nlc3NMaWtlIH0pLnByb2Nlc3M/LmVudiA/PyB7fTtcbn1cblxuLyoqXG4gKiBUaGUgUFJJTUFSWSAoY3VycmVudCwgbm9uLWRlcHJlY2F0ZWQpIG5hbWUgaW4gYW4ge0BsaW5rIEVudktleX0uXG4gKlxuICogVXNlIHRoaXMgd2hlbiBuYW1pbmcgYSB2YXJpYWJsZSBpbiBhIGxvZyBsaW5lIG9yIGVycm9yIHJhdGhlciB0aGFuIGluZGV4aW5nXG4gKiBga2V5c1swXWA6IGFuIHtAbGluayBFbnZLZXl9IG1heSBiZSBhIGJhcmUgc3RyaW5nLCBhbmQgYFwiVFVOTkVMX1hcIlswXWAgaXMgdGhlXG4gKiBjaGFyYWN0ZXIgYFwiVFwiYCwgd2hpY2ggcHJvZHVjZXMgYSBtZXNzYWdlIG5hbWluZyBhIHZhcmlhYmxlIHRoYXQgZG9lcyBub3RcbiAqIGV4aXN0LiBSZXR1cm5zIGBcIlwiYCBvbmx5IGZvciBhbiBlbXB0eSBsaXN0LCB3aGljaCBubyBjYWxsZXIgc2hvdWxkIGhhdmUuXG4gKlxuICogQGV4YW1wbGVcbiAqIGxvZ2dlci53YXJuKGAke2Vudi5uYW1lKEpXVF9TRUNSRVRfRU5WKX0gaXMgbm90IHNldGApO1xuICovXG5leHBvcnQgZnVuY3Rpb24gbmFtZShrZXlzOiBFbnZLZXkpOiBzdHJpbmcge1xuICByZXR1cm4gdHlwZW9mIGtleXMgPT09IFwic3RyaW5nXCIgPyBrZXlzIDogKGtleXNbMF0gPz8gXCJcIik7XG59XG5cbi8qKlxuICogRmlyc3Qgbm9uLWVtcHR5IHZhbHVlIGFtb25nIGBrZXlzYCwgdHJpbW1lZCwgZWxzZSBgbnVsbGAuXG4gKlxuICogU2V2ZXJhbCBuYW1lcyBmb3Igb25lIHNldHRpbmcgaXMgdGhlIG5vcm0gKGEgcGFja2FnZS1zcGVjaWZpYyB2YXJpYWJsZSBwbHVzXG4gKiB0aGUgRGF0YWJyaWNrcy1zdGFuZGFyZCBvbmUpLCBzbyBga2V5c2AgaXMgb3JkZXItc2Vuc2l0aXZlOiBlYXJsaWVyIG5hbWVzIHdpbi5cbiAqXG4gKiBAZXhhbXBsZVxuICogZW52LnRleHQoW1wiVEVBTVNfQVBQX0lEXCIsIFwiTUlDUk9TT0ZUX0FQUF9JRFwiXSk7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0ZXh0KGtleXM6IEVudktleSk6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBlbnYgPSBlbnZpcm9ubWVudCgpO1xuICBmb3IgKGNvbnN0IGtleSBvZiB0eXBlb2Yga2V5cyA9PT0gXCJzdHJpbmdcIiA/IFtrZXlzXSA6IGtleXMpIHtcbiAgICBjb25zdCB2YWx1ZSA9IHRyaW1Ub051bGwoZW52W2tleV0pO1xuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCkgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSBzdHJpbmcgc2V0dGluZzogYGNvbmZpZ3VyZWRgIHdoZW4gc2V0IGFuZCBub24tZW1wdHksIGVsc2UgdGhlIGZpcnN0XG4gKiBub24tZW1wdHkgdmFyaWFibGUgYW1vbmcgYGtleXNgLCBlbHNlIGBudWxsYC5cbiAqXG4gKiBAZXhhbXBsZVxuICogZW52LnN0cmluZyhjb25maWcuaG9zdCwgXCJTTVRQX0hPU1RcIik7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdHJpbmcoY29uZmlndXJlZDogdW5rbm93biwga2V5czogRW52S2V5KTogc3RyaW5nIHwgbnVsbCB7XG4gIHJldHVybiB0cmltVG9OdWxsKGNvbmZpZ3VyZWQpID8/IHRleHQoa2V5cyk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIGJvb2xlYW4gc2V0dGluZyB0aHJvdWdoIHtAbGluayB0b0Jvb2xlYW59LCBzbyB0aGUgbG9vc2Ugc3BlbGxpbmdzIGFuXG4gKiBlbnYgdmFyIGFjdHVhbGx5IGNhcnJpZXMgKGAxYCwgYG9uYCwgYHllc2AsIC4uLikgYXJlIGFjY2VwdGVkLiBSZXR1cm5zXG4gKiBgdW5kZWZpbmVkYCB3aGVuIG5laXRoZXIgc291cmNlIGlzIGludGVycHJldGFibGUsIGxldHRpbmcgdGhlIGNhbGxlciBwaWNrIGFcbiAqIGRlZmF1bHQgd2l0aCBgPz9gLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYuYm9vbGVhbihjb25maWcuZnV6enksIFwiV0VCX1NFQVJDSF9GVVpaWVwiKSA/PyB0cnVlO1xuICovXG5leHBvcnQgZnVuY3Rpb24gYm9vbGVhbihjb25maWd1cmVkOiB1bmtub3duLCBrZXlzOiBFbnZLZXkpOiBib29sZWFuIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHRvQm9vbGVhbihjb25maWd1cmVkKSA/PyB0b0Jvb2xlYW4odGV4dChrZXlzKSk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIHBvc2l0aXZlLW51bWJlciBzZXR0aW5nIHRoYXQgbWF5IGJlIGZyYWN0aW9uYWwgKGEgc2NvcmUgdGhyZXNob2xkLFxuICogYSByYXRpbyk6IGBjb25maWd1cmVkYCB3aGVuIGl0IGlzIGEgZmluaXRlIG51bWJlciBncmVhdGVyIHRoYW4gemVybywgZWxzZSB0aGVcbiAqIGZpcnN0IHZhcmlhYmxlIGFtb25nIGBrZXlzYCB0aGF0IHBhcnNlcyB0aGF0IHdheSwgZWxzZSBgZmFsbGJhY2tgLlxuICpcbiAqIHtAbGluayBwb3NpdGl2ZUludH0gaXMgdGhlIHJpZ2h0IGNob2ljZSBmb3IgYSBjb3VudDsgdGhpcyBvbmUga2VlcHMgdGhlXG4gKiBmcmFjdGlvbiwgc28gYSBgMC40YCB0aHJlc2hvbGQgZG9lcyBub3QgZmxvb3IgdG8gYDBgLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYucG9zaXRpdmVOdW1iZXIoY29uZmlnLmZ1enp5VGhyZXNob2xkLCBcIlNFQVJDSF9GVVpaWV9USFJFU0hPTERcIiwgMC40KTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvc2l0aXZlTnVtYmVyKGNvbmZpZ3VyZWQ6IHVua25vd24sIGtleXM6IEVudktleSwgZmFsbGJhY2s6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiB0b1Bvc2l0aXZlTnVtYmVyKGNvbmZpZ3VyZWQpID8/IHRvUG9zaXRpdmVOdW1iZXIodGV4dChrZXlzKSkgPz8gZmFsbGJhY2s7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIHBvc2l0aXZlLWludGVnZXIgc2V0dGluZyAoYSBwb3J0LCB0aW1lb3V0LCBwYWdlIHNpemUsIG9yIGNhcCk6XG4gKiBgY29uZmlndXJlZGAgd2hlbiBpdCBpcyBhIGZpbml0ZSBudW1iZXIgZ3JlYXRlciB0aGFuIHplcm8sIGVsc2UgdGhlIGZpcnN0XG4gKiB2YXJpYWJsZSBhbW9uZyBga2V5c2AgdGhhdCBwYXJzZXMgdGhhdCB3YXksIGVsc2UgYGZhbGxiYWNrYC4gRmxvb3JlZCwgc28gYVxuICogZnJhY3Rpb25hbCB2YWx1ZSBjYW4ndCBsZWFrIGludG8gYSBjb3VudC5cbiAqXG4gKiBBIG5vbi1udW1lcmljIG9yIG5vbi1wb3NpdGl2ZSB2YWx1ZSBpcyB0cmVhdGVkIGFzIGFic2VudCByYXRoZXIgdGhhbiBmYXRhbCAtXG4gKiB0aGVzZSBhcmUgY2VpbGluZ3MgYW5kIHRpbWVvdXRzIHdoZXJlIGEgc2FuZSBkZWZhdWx0IGJlYXRzIGEgYm9vdCBmYWlsdXJlLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYucG9zaXRpdmVJbnQoY29uZmlnLnRpbWVvdXRNcywgXCJTRUFSQ0hfVElNRU9VVF9NU1wiLCAzMF8wMDApO1xuICovXG5leHBvcnQgZnVuY3Rpb24gcG9zaXRpdmVJbnQoY29uZmlndXJlZDogdW5rbm93biwga2V5czogRW52S2V5LCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHRvUG9zaXRpdmVJbnQoY29uZmlndXJlZCkgPz8gdG9Qb3NpdGl2ZUludCh0ZXh0KGtleXMpKSA/PyBmYWxsYmFjaztcbn1cblxuZnVuY3Rpb24gdG9Qb3NpdGl2ZU51bWJlcih2YWx1ZTogdW5rbm93bik6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHBhcnNlZCA9IHRvTnVtYmVyKHZhbHVlKTtcbiAgcmV0dXJuIHBhcnNlZCAhPT0gdW5kZWZpbmVkICYmIHBhcnNlZCA+IDAgPyBwYXJzZWQgOiB1bmRlZmluZWQ7XG59XG5cbmZ1bmN0aW9uIHRvUG9zaXRpdmVJbnQodmFsdWU6IHVua25vd24pOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICBjb25zdCBwYXJzZWQgPSB0b1Bvc2l0aXZlTnVtYmVyKHZhbHVlKTtcbiAgcmV0dXJuIHBhcnNlZCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogTWF0aC5mbG9vcihwYXJzZWQpO1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSBsaXN0IHNldHRpbmcgdGhyb3VnaCB7QGxpbmsgcGFyc2VMaXN0fSwgc28gYW4gYXJyYXkgZnJvbSB0eXBlZFxuICogY29uZmlnIGFuZCBhIGBcImEsIGIgY1wiYCBlbnYgc3RyaW5nIG5vcm1hbGl6ZSBpZGVudGljYWxseS4gUmV0dXJucyBgW11gIHdoZW5cbiAqIG5laXRoZXIgc291cmNlIGhhcyBlbnRyaWVzLlxuICpcbiAqIEBleGFtcGxlXG4gKiBlbnYubGlzdChjb25maWcubW9kZWxGYWxsYmFja3MsIFwiV0VCX1NFQVJDSF9NT0RFTF9GQUxMQkFDS1NcIik7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsaXN0KFxuICBjb25maWd1cmVkOiBzdHJpbmcgfCByZWFkb25seSBzdHJpbmdbXSB8IHVuZGVmaW5lZCB8IG51bGwsXG4gIGtleXM6IEVudktleSxcbiAgdHJhbnNmb3JtPzogKGVudHJ5OiBzdHJpbmcpID0+IHN0cmluZyxcbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgZnJvbUNvbmZpZyA9IHBhcnNlTGlzdChjb25maWd1cmVkLCB0cmFuc2Zvcm0pO1xuICByZXR1cm4gZnJvbUNvbmZpZy5sZW5ndGggPiAwID8gZnJvbUNvbmZpZyA6IHBhcnNlTGlzdCh0ZXh0KGtleXMpLCB0cmFuc2Zvcm0pO1xufVxuIl19
@@ -2,12 +2,16 @@
2
2
  * Dependency-free object + iterable utilities.
3
3
  *
4
4
  * Value guards / coercions / shape types: {@link isRecord} narrows parsed JSON
5
- * to a record, {@link toBoolean} coerces loose truthy/falsy values, {@link
6
- * toDuration} parses `1h30m` / `2 milliseconds` to millis, {@link toDate}
7
- * coerces a date/ISO string/epoch number/relative duration, {@link optional}
5
+ * to a record, {@link toNumber} coerces a hand-typed numeral, {@link toBoolean}
6
+ * coerces loose truthy/falsy values, {@link toDuration} parses `1h30m` /
7
+ * `2 milliseconds` to millis, {@link toDate} coerces a date/ISO string/epoch
8
+ * number/relative duration (`toDate` and `toDuration` fall back to each other, so
9
+ * either accepts both readings), {@link optional}
8
10
  * spreads a field only when it is present, {@link deepEqual} compares
9
- * structurally, and {@link NameLike}/{@link NonFunctionKeys} describe object
10
- * shapes.
11
+ * structurally, {@link isSerializableValue} rejects anything a JSON round trip
12
+ * would lose or coerce, {@link toStableKey} canonicalizes a value so an identity
13
+ * can be derived from it, and {@link NameLike}/{@link NonFunctionKeys} describe
14
+ * object shapes.
11
15
  *
12
16
  * Iterable helpers: {@link generator} flattens mixed arguments; {@link sequence}
13
17
  * wraps source(s) in a lazy, `Array`-compatible {@link Sequence}. Every
@@ -352,6 +356,88 @@ export type NonFunctionKeys<T> = {
352
356
  * if (isRecord(parsed)) parsed.foo = 1;
353
357
  */
354
358
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
359
+ /** A JSON scalar. `undefined` is deliberately absent - `JSON.stringify` drops it. */
360
+ export type SerializablePrimitive = string | number | boolean | null;
361
+ /**
362
+ * Any value that survives a `JSON.stringify`/`JSON.parse` round trip unchanged.
363
+ *
364
+ * Use it instead of `unknown` on a boundary that will serialize its input - a
365
+ * message payload, a cache entry, a config blob written to disk - so a `Date`,
366
+ * a `Map`, or a class instance is a compile error at the call site rather than a
367
+ * receiver quietly getting a string or `{}`. {@link isSerializableValue} is the
368
+ * runtime half, for input the compiler cannot vouch for.
369
+ */
370
+ export type SerializableValue = SerializablePrimitive | SerializableValue[] | {
371
+ [key: string]: SerializableValue;
372
+ };
373
+ /**
374
+ * True when `value` survives a JSON round trip with no loss and no coercion.
375
+ *
376
+ * Stricter than "`JSON.stringify` did not throw", because that succeeds while
377
+ * silently CHANGING the value: a `Date` becomes a string, `NaN` and `Infinity`
378
+ * become `null`, a `Map` becomes `{}`, and `undefined` disappears from an object
379
+ * or turns into `null` inside an array. Each of those reaches the far side as
380
+ * something other than what was sent, so all of them are rejected here.
381
+ *
382
+ * Rejected: non-finite numbers, `undefined`, functions, symbols, bigints, class
383
+ * instances and anything else with a prototype other than `Object.prototype` or
384
+ * `null` (`Date`, `Map`, `Set`, `RegExp`, `Buffer`), and any object graph
385
+ * containing a cycle. Accepted: strings, booleans, `null`, finite numbers, plain
386
+ * objects, arrays, and nestings of those.
387
+ *
388
+ * Narrows to {@link SerializableValue} rather than asserting, so it also serves
389
+ * as the validator for untrusted input - a request body, a decoded notification
390
+ * payload - where the answer should be a 400 and not a throw. Never throws.
391
+ *
392
+ * Distinct from {@link deepEqual}'s notion of comparable: that one HANDLES
393
+ * `Date`/`Map`/`Set` structurally, while this one rejects them precisely because
394
+ * JSON cannot carry them.
395
+ *
396
+ * @param ancestors Cycle-detection set for the recursive walk. Internal; callers
397
+ * pass one value.
398
+ *
399
+ * @example
400
+ * isSerializableValue({ a: [1, "x", null] }); // true
401
+ * isSerializableValue({ at: new Date() }); // false - would become a string
402
+ * isSerializableValue(Number.NaN); // false - would become null
403
+ */
404
+ export declare function isSerializableValue(value: unknown, ancestors?: Set<object>): value is SerializableValue;
405
+ /**
406
+ * Canonical string for a structured value, for deriving a STABLE IDENTITY from
407
+ * it - an advisory-lock id, a channel name, a cache key.
408
+ *
409
+ * The guarantee is two-way, which is what makes it safe to hash: values that
410
+ * should share an identity produce the same string (object key order does not
411
+ * matter), and values that should not are never conflated. Every token carries
412
+ * its type, so `1` and `"1"` differ; a string carries its length, so
413
+ * `["a", "bc"]` and `["ab", "c"]` differ; arrays keep order while object keys
414
+ * are sorted.
415
+ *
416
+ * `JSON.stringify` cannot do this job - key order leaks in, `undefined` vanishes,
417
+ * `1` and `"1"` collide after quoting is stripped, and a cycle throws a
418
+ * `TypeError` naming neither the value nor the caller's intent.
419
+ *
420
+ * Deliberately strict where a silent answer would be a WRONG identity rather
421
+ * than a missing one, since two callers disagreeing about a lock or a channel is
422
+ * invisible until it corrupts something. Throws `TypeError` on a cycle, on a
423
+ * non-finite number (`NaN` is not equal to itself, so it cannot have a stable
424
+ * identity), and on a `function` or `symbol` (no meaningful value identity).
425
+ * `undefined` and `null` are accepted as distinct tokens.
426
+ *
427
+ * `Date` is canonicalized by instant, unlike the hash canonicalizer in
428
+ * `./hash.ts`, which folds every `Date` onto one token. Prefer this function when
429
+ * distinctness is a correctness requirement; prefer `hash.fnvHash` when a short,
430
+ * collision-tolerant digest is enough.
431
+ *
432
+ * @param value - The value to canonicalize.
433
+ * @param seen - Cycle-detection set for the recursive walk. Internal; callers
434
+ * pass one value.
435
+ *
436
+ * @example
437
+ * toStableKey({ a: 1, b: 2 }) === toStableKey({ b: 2, a: 1 }); // true
438
+ * toStableKey(1) !== toStableKey("1"); // true
439
+ */
440
+ export declare function toStableKey(value: unknown, seen?: Set<object>): string;
355
441
  /**
356
442
  * `{ [key]: value }` when `value` is present, otherwise `undefined` - so an
357
443
  * absent optional field stays ABSENT when spread, rather than becoming an
@@ -368,6 +454,69 @@ export declare function isRecord(value: unknown): value is Record<string, unknow
368
454
  * };
369
455
  */
370
456
  export declare function optional<K extends string, V>(key: K, value: V | null | undefined): Record<K, V> | undefined;
457
+ /**
458
+ * Options for {@link toNumber}.
459
+ *
460
+ * Both switches turn OFF a leniency that is helpful for a hand-typed setting but
461
+ * wrong when the string's other characters carry meaning. {@link toDate} and
462
+ * {@link toDuration} disable both, since a space inside `2026 08 02` is a field
463
+ * separator and a percent has no epoch or millisecond reading.
464
+ */
465
+ export interface ToNumberOptions {
466
+ /**
467
+ * Whether internal digit-group separators are stripped, so `"1,000"` and
468
+ * `"1 000"` read as `1000`. Defaults to `true`.
469
+ *
470
+ * Placement is not validated when enabled, so `"1,00,0"` also reads as `1000`.
471
+ * Disable it when whitespace or a comma delimits FIELDS rather than grouping
472
+ * digits, because stripping them silently fuses those fields into one number.
473
+ */
474
+ separators?: boolean;
475
+ /**
476
+ * Whether a trailing percent sign divides the result by `100`, so `"25%"` reads
477
+ * as `0.25`. Defaults to `true`.
478
+ *
479
+ * Disable it where a percentage has no meaning, so `"25%"` is a miss rather
480
+ * than a number two orders of magnitude away from what the text says.
481
+ */
482
+ percent?: boolean;
483
+ }
484
+ /**
485
+ * Coerce a loose numeric value to a real, FINITE `number`, or `undefined` when it
486
+ * carries no numeric meaning.
487
+ *
488
+ * The one place a hand-typed number is interpreted, alongside {@link toBoolean},
489
+ * {@link toDate}, and {@link toDuration}. Reach for it instead of `Number(x)` or a
490
+ * hand-rolled numeric regex: bare `Number` maps `""`, `null`, `[]`, and
491
+ * whitespace to `0` and anything else to `NaN`, so a caller has to re-check the
492
+ * result every time, and ad hoc regexes tend to drift in what they accept.
493
+ *
494
+ * Accepts: a finite `number`; a `bigint`; or a decimal string with an optional
495
+ * leading sign, leading/trailing whitespace, whitespace after the sign,
496
+ * digit-group separators (`"1,000"`, `"1 000"`), a bare fraction (`".5"`), a
497
+ * trailing point (`"1."`), scientific notation (`"1e3"`), and an optional
498
+ * trailing percent sign (`"25%"`, `"1.5 %"`). A trailing `%` divides the parsed
499
+ * value by `100`, so `"25%"` becomes `0.25`.
500
+ *
501
+ * Separators are stripped without validating their placement, so `"1,00,0"` reads
502
+ * as `1000`; this is a coercion for hand-typed configuration, not a locale-aware
503
+ * validator. Anything else, including `NaN`, `Infinity`, an empty or
504
+ * whitespace-only string, `null`, `undefined`, a boolean, multiple signs,
505
+ * malformed exponents, misplaced percent signs, or `"12px"`, returns `undefined`.
506
+ *
507
+ * Returns `undefined` rather than throwing, matching the other coercions, so it
508
+ * composes naturally with `??` fallbacks.
509
+ *
510
+ * @example
511
+ * toNumber("1,000"); // 1000
512
+ * toNumber(" -2.5 "); // -2.5
513
+ * toNumber("1e3"); // 1000
514
+ * toNumber("12.5 %"); // 0.125
515
+ * toNumber(""); // undefined (Number("") would be 0)
516
+ * toNumber("12px"); // undefined
517
+ * toNumber("1 000", { separators: false }); // undefined
518
+ */
519
+ export declare function toNumber(value: unknown, options?: ToNumberOptions): number | undefined;
371
520
  /**
372
521
  * Coerce a loose boolean-ish value to a real `boolean`, or `undefined`
373
522
  * when it can't be interpreted. Recognizes `true`/`t`/`on`/`1`/`yes`/`y`
@@ -375,6 +524,45 @@ export declare function optional<K extends string, V>(key: K, value: V | null |
375
524
  * well as the numbers `1` and `0`.
376
525
  */
377
526
  export declare function toBoolean(value: unknown): boolean | undefined;
527
+ /**
528
+ * Options for {@link toDate}.
529
+ *
530
+ * The mirror of {@link ToDurationOptions}: each function can fall back to the
531
+ * other, so each has one switch turning that fallback off.
532
+ */
533
+ export interface ToDateOptions {
534
+ /**
535
+ * Whether a {@link toDuration} expression is read as an instant relative to
536
+ * now, so `-7d` and `7 days ago` become `now - 7 days`. Defaults to `true`.
537
+ *
538
+ * Set `false` when the value must be a real date and a relative expression
539
+ * should be a miss - a stored timestamp, a user-supplied `Date` header, an
540
+ * `expires_at` field - since a duration silently resolving against the current
541
+ * clock makes the same input mean something different on every call. Also what
542
+ * {@link toDuration} passes when it recurses, so the two cannot bounce a value
543
+ * between them forever.
544
+ */
545
+ parseDuration?: boolean;
546
+ }
547
+ /**
548
+ * Options for {@link toDuration}.
549
+ *
550
+ * The mirror of {@link ToDateOptions}: each function can fall back to the other,
551
+ * so each has one switch turning that fallback off.
552
+ */
553
+ export interface ToDurationOptions {
554
+ /**
555
+ * Whether a {@link toDate} value is read as the signed offset from now, so
556
+ * `2026-08-02` becomes however long until (or since) that instant. Defaults to
557
+ * `true`.
558
+ *
559
+ * Set `false` when only a length of time is meaningful - a timeout, a poll
560
+ * interval, a cache TTL - because a date would otherwise yield a plausible but
561
+ * wrong number that also drifts with the clock. Also what {@link toDate} passes
562
+ * when it recurses.
563
+ */
564
+ parseDate?: boolean;
565
+ }
378
566
  /**
379
567
  * Coerce a loose duration to MILLISECONDS, or `undefined` when it can't be
380
568
  * interpreted.
@@ -396,14 +584,22 @@ export declare function toBoolean(value: unknown): boolean | undefined;
396
584
  * returning `1` silently would be worse than returning nothing - which is also
397
585
  * what keeps {@link toDate} from mistaking `1 Jan 2026` for a duration.
398
586
  *
587
+ * A value that is not a duration at all is offered to {@link toDate} and, when it
588
+ * IS a date, read as the signed offset from now (`date - now`), which makes the
589
+ * two functions inverses: a past instant is negative, a future one positive.
590
+ * `options.parseDate: false` turns that off when only a length of time makes
591
+ * sense - see {@link ToDurationOptions}.
592
+ *
399
593
  * @example
400
594
  * toDuration("30s"); // 30_000
401
595
  * toDuration("1 hour 30 minutes"); // 5_400_000
402
596
  * toDuration("-7 days"); // -604_800_000
403
597
  * toDuration("2 weeks ago"); // -1_209_600_000
598
+ * toDuration("2026-08-02"); // ms from now to that instant
599
+ * toDuration("2026-08-02", { parseDate: false }); // undefined
404
600
  * toDuration("soon"); // undefined
405
601
  */
406
- export declare function toDuration(value: unknown): number | undefined;
602
+ export declare function toDuration(value: unknown, options?: ToDurationOptions): number | undefined;
407
603
  /**
408
604
  * Coerce a loose date-ish value to a real `Date`, or `undefined` when it can't be
409
605
  * interpreted. Accepts, in this order:
@@ -423,6 +619,10 @@ export declare function toDuration(value: unknown): number | undefined;
423
619
  * 1.7 billion years out. Numeric strings are therefore routed to the epoch path,
424
620
  * never to `Date.parse`.
425
621
  *
622
+ * The duration fallback runs LAST, after `Date.parse`, so a real date is never
623
+ * mistaken for an offset. `options.parseDuration: false` removes it entirely when
624
+ * the value must be an absolute instant - see {@link ToDateOptions}.
625
+ *
426
626
  * Like {@link toBoolean} this NEVER throws and returns `undefined` for anything
427
627
  * uninterpretable, so a caller decides whether a bad value is fatal, a warning,
428
628
  * or a fallback.
@@ -433,9 +633,10 @@ export declare function toDuration(value: unknown): number | undefined;
433
633
  * toDate("1785697899"); // seconds -> 2026-08-02T...
434
634
  * toDate(1785697899000); // millis -> the same instant
435
635
  * toDate("30 days ago"); // now - 30d
636
+ * toDate("30 days ago", { parseDuration: false }); // undefined
436
637
  * toDate("nope"); // undefined
437
638
  */
438
- export declare function toDate(value: unknown): Date | undefined;
639
+ export declare function toDate(value: unknown, options?: ToDateOptions): Date | undefined;
439
640
  /**
440
641
  * Structural deep-equality with an optional custom comparator.
441
642
  *