@jarenjs/core 0.49.2 → 0.66.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/ARCHITECTURE.md CHANGED
@@ -161,6 +161,19 @@ flowchart TB
161
161
 
162
162
  subgraph FunctionModule["Function Utilities"]
163
163
  FunctionUtil["function.js<br/>trueThat, falseThat"]
164
+ AsyncUtil["async.js<br/>Bounded ordered asynchronous map, awaited sink"]
165
+ end
166
+
167
+ subgraph RandomModule["Randomness"]
168
+ RandomUtil["random.js<br/>Seeded generator, integer draw, shuffle, distinct draw"]
169
+ end
170
+
171
+ subgraph RuntimeModule["Host runtime"]
172
+ RuntimeRecord["runtime.js<br/>The runtime record: clock, identifiers, randomness, zone provider"]
173
+ end
174
+
175
+ subgraph StatsModule["Statistics"]
176
+ StatsUtil["stats.js<br/>Mean, sample variance, median, named quantile"]
164
177
  end
165
178
 
166
179
  subgraph StringModules["String &amp; Scanning"]
@@ -628,9 +641,9 @@ direction is *not* claimed, because WKT whitespace, the `M` measure and number
628
641
  spelling are normalized on the way through.
629
642
 
630
643
  Against [`wellknown`](https://www.npmjs.com/package/wellknown), the established
631
- WKT↔GeoJSON converter (`npm run benchmark:geo`, Node <!--bm:geo.node-->v24.19.0<!--/bm-->):
644
+ WKT↔GeoJSON converter (`npm run benchmark:geo`, Node <!--fact:geo.node-->v24.19.0<!--/fact-->):
632
645
 
633
- <!--bm:geo.wktTable-->
646
+ <!--fact:geo.wktTable-->
634
647
  | scenario | Jaren | rival | ratio |
635
648
  |---|---|---|---|
636
649
  | wkt parse (POINT) | 431.1 ns | 1.31 µs (wellknown) | **3.1×** |
@@ -638,7 +651,7 @@ WKT↔GeoJSON converter (`npm run benchmark:geo`, Node <!--bm:geo.node-->v24.19.
638
651
  | wkt stringify (2000-vertex polygon) | 163.40 µs | 229.70 µs (wellknown) | **1.4×** |
639
652
  | wkt validate (POINT) | 242.7 ns | 1.28 µs (wellknown) | **5.3×** |
640
653
  | wkt validate (2000-vertex polygon) | 496.00 µs | 1.90 ms (wellknown) | **3.8×** |
641
- <!--/bm-->
654
+ <!--/fact-->
642
655
 
643
656
  The last two rows are the ones the sink exists for: `wellknown` has no
644
657
  predicate, so validating means parsing and throwing the geometry away. And the
@@ -677,7 +690,7 @@ RFC 7946 removed coordinate-reference-system support and mandates WGS 84, so
677
690
  there is no SRID table and no reprojection: conformance removes the need rather
678
691
  than an omission hiding it.
679
692
 
680
- **Measured against the field** (`npm run benchmark:geo`, Node <!--bm:geo.node-->v24.19.0<!--/bm-->). Every
693
+ **Measured against the field** (`npm run benchmark:geo`, Node <!--fact:geo.node-->v24.19.0<!--/fact-->). Every
681
694
  scenario asserts result equivalence before any timing, and the harness refuses
682
695
  to print a table if the engines disagree: distance, area, length and bounding
683
696
  box come out *bit-identical* to Turf, containment agrees on a 400-point sweep,
@@ -685,7 +698,7 @@ and the index returns exactly Flatbush's answer on 200 queries. Ratios are the
685
698
  rival's time over this kernel's, so above 1 means Jaren is faster; the rows
686
699
  below derive from the committed measurement and are refreshed with it.
687
700
 
688
- <!--bm:geo.table-->
701
+ <!--fact:geo.table-->
689
702
  | scenario | Jaren | rival | ratio |
690
703
  |---|---|---|---|
691
704
  | distance (two positions) | 28.3 ns | 111.0 ns (turf) | **3.9×** |
@@ -700,11 +713,11 @@ below derive from the committed measurement and are refreshed with it.
700
713
  | index build (100k boxes) | 12.70 ms | 10.62 ms (flatbush) | 0.8× |
701
714
  | index probe (100k boxes) | 602.3 ns | 668.9 ns (flatbush) | **1.1×** |
702
715
  | linear scan (100k boxes, no index) | 141.50 µs | — | — |
703
- <!--/bm-->
716
+ <!--/fact-->
704
717
 
705
- The losses are kept, and named by the same measurement: on the committed run <!--bm:geo.losses-->three rows lose to a rival: point in polygon (2000-vertex) at 0.5× (turf), bounding box (2000-vertex) at 0.9× (turf), index build (100k boxes) at 0.8× (flatbush)<!--/bm-->.
718
+ The losses are kept, and named by the same measurement: on the committed run <!--fact:geo.losses-->three rows lose to a rival: point in polygon (2000-vertex) at 0.5× (turf), bounding box (2000-vertex) at 0.9× (turf), index build (100k boxes) at 0.8× (flatbush)<!--/fact-->.
706
719
  **Point-in-polygon on a large ring runs at about half Turf's speed**
707
- (<!--bm:geo.pip2000-->0.5×<!--/bm--> at 2000 vertices) because every edge that could
720
+ (<!--fact:geo.pip2000-->0.5×<!--/fact--> at 2000 vertices) because every edge that could
708
721
  matter goes through the exact orientation predicate, where Turf uses naive
709
722
  floating-point arithmetic. That is the trade this module exists to make — it
710
723
  is the difference between a containment test that is right on near-collinear
@@ -713,7 +726,7 @@ predicate on edges that cannot affect the answer, which took this from 0.2×
713
726
  to about 0.5×; the rest is the predicate itself, and the campaign that made
714
727
  geography reachable across the suite never traded it away. The bounding box
715
728
  of the same ring and the index build against Flatbush sit within a few tenths
716
- of level (<!--bm:geo.bbox2000-->0.9×<!--/bm--> and <!--bm:geo.indexBuild-->0.8×<!--/bm-->) and move
729
+ of level (<!--fact:geo.bbox2000-->0.9×<!--/fact--> and <!--fact:geo.indexBuild-->0.8×<!--/fact-->) and move
717
730
  between runs and Node versions; they are published as measured rather than
718
731
  rounded to a win.
719
732
 
@@ -825,6 +838,36 @@ findSlots(shifts, { duration: 'PT30M' }).length; // 16
825
838
  createIntervalIndex(shifts).at(4 * 3600_000); // the second shift only: [start, end)
826
839
  ```
827
840
 
841
+ ### 5e. Runtime record (`runtime.js`)
842
+
843
+ The one record a host hands to every subsystem that needs a host fact:
844
+ `createRuntime({ now, uuid, random, zoneProvider })`, frozen, defaulting
845
+ member for member to the platform's own (`Date.now`, `crypto.randomUUID`,
846
+ `Math.random`, and no zone provider — a named zone stays a refusal). The
847
+ store (query deadlines included), the job engine, the migration runner, the
848
+ contract bindings and the contract memory ledger take it as `runtime`; a
849
+ subsystem's own explicit option wins over the record's member,
850
+ which wins over the built-in default, so adopting the record changes nothing
851
+ observable and a deterministic run — a fixed clock, a seeded generator, a
852
+ counting identifier — is configured once. It is a type plus a freeze: this
853
+ module reads no clock and draws no number of its own, and it reaches hosts,
854
+ never query compilation, because a compiled query is cached by document
855
+ identity and there is no `now` in the kernel for it to feed.
856
+
857
+ ```javascript
858
+ import { createRuntime } from '@jarenjs/core/runtime';
859
+ import { mulberry32 } from '@jarenjs/core/random';
860
+
861
+ let n = 0;
862
+ const runtime = createRuntime({
863
+ now: () => 1_700_000_000_000,
864
+ uuid: () => `id-${++n}`,
865
+ random: mulberry32(2026),
866
+ });
867
+ runtime.zoneProvider; // null — the one member left at its default
868
+ createRuntime({ clock: Date.now }); // TypeError: a runtime record has 'now', 'uuid', 'random', 'zoneProvider', not 'clock'
869
+ ```
870
+
828
871
  ### 6. Text Module (`text/`)
829
872
 
830
873
  Comprehensive string format validation organized by domain.
package/README.md CHANGED
@@ -13,6 +13,10 @@ None of it depends on JSON Schema: every module can be used standalone in any Ja
13
13
  | `@jarenjs/core/object` | deep equality (`equalsDeep`, JSON-only `equalsJson`), the `isJsonObject` and deep `isJsonValue` predicates, `__proto__`-safe `setObjectMember`, `deepFreeze`, map/set merging |
14
14
  | `@jarenjs/core/string` | Unicode string helpers (`countCodePoints`, `compareCodePoints`, ...), cached regex compilation, the suite's one content hash (`fnv1a` and the `hashContent` fingerprint over it) and `kebabCase` |
15
15
  | `@jarenjs/core/cache` | the bounded LRU (`createBoundedCache`), the reference-keyed `createWeakCache`, and `createSemanticCache` — keyed by what a value IS, for caches whose entries decide a result |
16
+ | `@jarenjs/core/random` | the suite's one seeded generator (`mulberry32`, pinned sequence, ToUint32 seed) and the draws built on it: `randomInt` over a half-open range, in-place Fisher–Yates `shuffle`, and `drawDistinct` — `k` distinct indices from one stream |
17
+ | `@jarenjs/core/runtime` | the runtime record — `createRuntime({ now, uuid, random, zoneProvider })`, frozen, defaulting member for member to the platform's own (`Date.now`, `crypto.randomUUID`, `Math.random`, no zone provider) — that the store (its query deadlines included), the jobs engine, the migration runner, every contract binding and the contract memory ledger take as `runtime`, so a deterministic run is configured once; a subsystem's own explicit option wins over the record, and the record reaches hosts, never query compilation |
18
+ | `@jarenjs/core/stats` | descriptive statistics over a sample: `mean`, sample `variance`/`stddev`, the midpoint `median`, and `quantile(values, p, { method })` — `p` on 0..1 under a NAMED rule, `'nearest-rank'` or `'linear'`, because a default would decide silently; an empty sample answers `undefined`, never `0` |
19
+ | `@jarenjs/core/async` | `mapConcurrent(items, limit, worker, { signal })` — the bounded ordered asynchronous map: never more than `limit` workers in flight, results in input order, a rejection or an abort stops dispatch and drains the lanes before the map rejects, so nothing is still running when it settles; and `createAwaitedSink(sink)` — the suite's one write serializer: every `write` waits for the previous one to settle (a sink may answer a promise — a socket waiting for `drain`, a stream waiting for the consumer's pull), `end` waits for every write, `abort` rejects what is still queued at once, one failure stops everything behind it, and a synchronous sink stays on a no-promise fast path |
16
20
  | `@jarenjs/core/chunk` | cutting a value down to size: `sizeOf` (the suite's one size rule — a string is its length, anything else its JSON), `excerpt`, `truncate`, and `chunkText` by size, line or separator with offsets that locate a piece in its source |
17
21
  | `@jarenjs/core/scan` | char-code constants and predicates for recursive-descent parsers |
18
22
  | `@jarenjs/core/message` | the message template/catalog compiler shared by the validator and the form layer |
@@ -98,7 +102,7 @@ Four design decisions carry the module:
98
102
  - **Orientation is computed exactly** (Shewchuk's adaptive predicates): a naive floating-point determinant returns the *wrong sign* on near-collinear input, which makes containment contradict itself. Every ring winding and point-in-polygon answer rests on this sign, and the deliberate cost is on the [benchmark page](https://jklarenbeek.github.io/jarenjs/#/benchmarks?suite=geo).
99
103
  - **Measurement is spherical, drawing is projected, and the two never mix.** A Euclidean norm on raw degrees is 64% wrong over 1 km at Dutch latitudes, so `haversineDistance`/`sphericalRingArea` work on the sphere (`equirectDistance` is the cheap screening form for rejecting candidates first), while `projectMercator`/`fitMercator` and `simplifyLine`/`simplifyRing` exist for renderers — never measure on a projected coordinate.
100
104
  - **Validity is a separate concern from traversal, and a box is refused rather than made too small.** `eachPosition`, `bboxOf`, `centroidOf` and friends measure without judging (a ring is read as closed whether or not it is); a box over any non-finite coordinate is `null`, never a plausible box that misses its input; and boxes never cross the antimeridian — cut the geometry at ±180° as RFC 7946 §3.1.9 asks, and every containment test and index probe is right. `isValidGeoJson` (structure plus the ring closure a JSON Schema provably cannot express), `isValidWkt` and `isValidGeohash` are the one-call judgments that back the `geoFormats` group in [`@jarenjs/formats`](../formats), next to the full GeoJSON meta-schema artifacts in [`@jarenjs/json`](../json).
101
- - **WKT is one grammar walk with two entry points.** `isValidWkt` and `wktToGeoJson` run the *same* scan, parameterized by a sink that is absent for the predicate and present for the parser — so the `wkt` format tester (which runs per value in the validator and per keystroke in the form layer) allocates nothing, and the two cannot drift apart. A committed corpus asserts `isValidWkt(s) === (wktToGeoJson(s) !== null)` for all 181 entries, malformed half included. `geoJsonToWkt` writes the string back, and `wktToGeoJson(geoJsonToWkt(g))` returns `g`; the text direction is *not* claimed, because whitespace, the `M` measure and number spelling are normalized. Against [`wellknown`](https://www.npmjs.com/package/wellknown) the parse is <!--bm:geo.wktParsePoint-->3.1×<!--/bm--> faster on a `POINT` and the yes-or-no answer <!--bm:geo.wktValidatePoint-->5.3×<!--/bm--> — and wellknown *validates less* while being slower, which is the honest framing; the table and every language difference are in [ARCHITECTURE](./ARCHITECTURE.md). The kernel's own losses are published there too, derived from the same run: <!--bm:geo.losses-->three rows lose to a rival: point in polygon (2000-vertex) at 0.5× (turf), bounding box (2000-vertex) at 0.9× (turf), index build (100k boxes) at 0.8× (flatbush)<!--/bm-->.
105
+ - **WKT is one grammar walk with two entry points.** `isValidWkt` and `wktToGeoJson` run the *same* scan, parameterized by a sink that is absent for the predicate and present for the parser — so the `wkt` format tester (which runs per value in the validator and per keystroke in the form layer) allocates nothing, and the two cannot drift apart. A committed corpus asserts `isValidWkt(s) === (wktToGeoJson(s) !== null)` for all 181 entries, malformed half included. `geoJsonToWkt` writes the string back, and `wktToGeoJson(geoJsonToWkt(g))` returns `g`; the text direction is *not* claimed, because whitespace, the `M` measure and number spelling are normalized. Against [`wellknown`](https://www.npmjs.com/package/wellknown) the parse is <!--fact:geo.wktParsePoint-->3.1×<!--/fact--> faster on a `POINT` and the yes-or-no answer <!--fact:geo.wktValidatePoint-->5.3×<!--/fact--> — and wellknown *validates less* while being slower, which is the honest framing; the table and every language difference are in [ARCHITECTURE](./ARCHITECTURE.md). The kernel's own losses are published there too, derived from the same run: <!--fact:geo.losses-->three rows lose to a rival: point in polygon (2000-vertex) at 0.5× (turf), bounding box (2000-vertex) at 0.9× (turf), index build (100k boxes) at 0.8× (flatbush)<!--/fact-->.
102
106
 
103
107
  The spatial query operators (`$distance`, `$within`, `$geohash`, spatial joins over the box index) live in the query engine in [`@jarenjs/json`](../json); the streaming map chart that draws a FeatureCollection with bounded memory lives in [`@jarenjs/charts`](../../components/charts). The full module reference is [docs/GEO.md](./docs/GEO.md).
104
108
 
@@ -187,6 +191,110 @@ t({ comparison: '>=', limit: 18 }); // 'must be >= 18'
187
191
 
188
192
  The JSON addressing and query standards — JSON validation helpers, JSON Pointer (RFC 6901), the compiling JSONPath engine (RFC 9535), and the Jaren JSON Query language with its XQuery front-end — live in [`@jarenjs/json`](../json). This package supplies their foundations: the char-code scanner (`@jarenjs/core/scan`), `equalsJson` deep equality, code-point ordering, and the I-Regexp (RFC 9485) toolbox.
189
193
 
194
+ ## Exports
195
+
196
+ Every subpath a consumer can import, derived from the manifest by
197
+ `npm run docs:derive` (`npm run docs:check` fails when the two drift):
198
+
199
+ <!--fact:exports.core-->
200
+ | Import | Kind | Declarations |
201
+ |---|---|---|
202
+ | `@jarenjs/core` | JavaScript | declared |
203
+ | `@jarenjs/core/array` | JavaScript | declared |
204
+ | `@jarenjs/core/async` | JavaScript | declared |
205
+ | `@jarenjs/core/bigint` | JavaScript | declared |
206
+ | `@jarenjs/core/cache` | JavaScript | declared |
207
+ | `@jarenjs/core/chunk` | JavaScript | declared |
208
+ | `@jarenjs/core/color` | JavaScript | declared |
209
+ | `@jarenjs/core/dates` | JavaScript | declared |
210
+ | `@jarenjs/core/errors` | JavaScript | declared |
211
+ | `@jarenjs/core/dates/civil` | JavaScript | declared |
212
+ | `@jarenjs/core/dates/duration` | JavaScript | declared |
213
+ | `@jarenjs/core/dates/format` | JavaScript | declared |
214
+ | `@jarenjs/core/dates/index` | JavaScript | declared |
215
+ | `@jarenjs/core/dates/parse` | JavaScript | declared |
216
+ | `@jarenjs/core/dates/rfc3339` | JavaScript | declared |
217
+ | `@jarenjs/core/dates/ticks` | JavaScript | declared |
218
+ | `@jarenjs/core/float` | JavaScript | declared |
219
+ | `@jarenjs/core/geo` | JavaScript | declared |
220
+ | `@jarenjs/core/geo/angle` | JavaScript | declared |
221
+ | `@jarenjs/core/geo/bbox` | JavaScript | declared |
222
+ | `@jarenjs/core/geo/distance` | JavaScript | declared |
223
+ | `@jarenjs/core/geo/geohash` | JavaScript | declared |
224
+ | `@jarenjs/core/geo/geojson` | JavaScript | declared |
225
+ | `@jarenjs/core/geo/index` | JavaScript | declared |
226
+ | `@jarenjs/core/geo/index-tree` | JavaScript | declared |
227
+ | `@jarenjs/core/geo/mercator` | JavaScript | declared |
228
+ | `@jarenjs/core/geo/predicates` | JavaScript | declared |
229
+ | `@jarenjs/core/geo/ring` | JavaScript | declared |
230
+ | `@jarenjs/core/geo/simplify` | JavaScript | declared |
231
+ | `@jarenjs/core/geo/valid` | JavaScript | declared |
232
+ | `@jarenjs/core/geo/wkt` | JavaScript | declared |
233
+ | `@jarenjs/core/vector` | JavaScript | declared |
234
+ | `@jarenjs/core/function` | JavaScript | declared |
235
+ | `@jarenjs/core/integer` | JavaScript | declared |
236
+ | `@jarenjs/core/message` | JavaScript | declared |
237
+ | `@jarenjs/core/number` | JavaScript | declared |
238
+ | `@jarenjs/core/object` | JavaScript | declared |
239
+ | `@jarenjs/core/random` | JavaScript | declared |
240
+ | `@jarenjs/core/runtime` | JavaScript | declared |
241
+ | `@jarenjs/core/scan` | JavaScript | declared |
242
+ | `@jarenjs/core/schema` | JavaScript | declared |
243
+ | `@jarenjs/core/series` | JavaScript | declared |
244
+ | `@jarenjs/core/series/asof` | JavaScript | declared |
245
+ | `@jarenjs/core/series/bucket` | JavaScript | declared |
246
+ | `@jarenjs/core/series/downsample` | JavaScript | declared |
247
+ | `@jarenjs/core/series/index` | JavaScript | declared |
248
+ | `@jarenjs/core/series/interval` | JavaScript | declared |
249
+ | `@jarenjs/core/series/interval-index` | JavaScript | declared |
250
+ | `@jarenjs/core/series/normalize` | JavaScript | declared |
251
+ | `@jarenjs/core/series/rolling` | JavaScript | declared |
252
+ | `@jarenjs/core/series/selector` | JavaScript | declared |
253
+ | `@jarenjs/core/series/zone` | JavaScript | declared |
254
+ | `@jarenjs/core/stats` | JavaScript | declared |
255
+ | `@jarenjs/core/string` | JavaScript | declared |
256
+ | `@jarenjs/core/text` | JavaScript | declared |
257
+ | `@jarenjs/core/text/base64` | JavaScript | declared |
258
+ | `@jarenjs/core/text/basic` | JavaScript | declared |
259
+ | `@jarenjs/core/text/email` | JavaScript | declared |
260
+ | `@jarenjs/core/text/host` | JavaScript | declared |
261
+ | `@jarenjs/core/text/i18n` | JavaScript | declared |
262
+ | `@jarenjs/core/text/identifiers` | JavaScript | declared |
263
+ | `@jarenjs/core/text/index` | JavaScript | declared |
264
+ | `@jarenjs/core/text/iregexp` | JavaScript | declared |
265
+ | `@jarenjs/core/text/misc` | JavaScript | declared |
266
+ | `@jarenjs/core/text/punycode` | JavaScript | declared |
267
+ | `@jarenjs/core/text/sse` | JavaScript | declared |
268
+ | `@jarenjs/core/math` | JavaScript | declared |
269
+ | `@jarenjs/core/math/float64` | JavaScript | declared |
270
+ | `@jarenjs/core/math/format` | JavaScript | declared |
271
+ | `@jarenjs/core/math/index` | JavaScript | declared |
272
+ | `@jarenjs/core/math/int32` | JavaScript | declared |
273
+ | `@jarenjs/core/math/mat4` | JavaScript | declared |
274
+ | `@jarenjs/core/math/project` | JavaScript | declared |
275
+ | `@jarenjs/core/math/solve` | JavaScript | declared |
276
+ | `@jarenjs/core/math/vec2f64` | JavaScript | declared |
277
+ | `@jarenjs/core/math/vec2i32` | JavaScript | declared |
278
+ | `@jarenjs/core/math/vec3f64` | JavaScript | declared |
279
+ | `@jarenjs/core/math/word` | JavaScript | declared |
280
+ | `@jarenjs/core/finance` | JavaScript | declared |
281
+ | `@jarenjs/core/finance/amortization` | JavaScript | declared |
282
+ | `@jarenjs/core/finance/bond` | JavaScript | declared |
283
+ | `@jarenjs/core/finance/cashflow` | JavaScript | declared |
284
+ | `@jarenjs/core/finance/depreciation` | JavaScript | declared |
285
+ | `@jarenjs/core/finance/index` | JavaScript | declared |
286
+ | `@jarenjs/core/finance/indicators` | JavaScript | declared |
287
+ | `@jarenjs/core/finance/interest` | JavaScript | declared |
288
+ | `@jarenjs/core/finance/returns` | JavaScript | declared |
289
+ | `@jarenjs/core/finance/tvm` | JavaScript | declared |
290
+ | `@jarenjs/core/convert` | JavaScript | declared |
291
+ | `@jarenjs/core/convert/convert` | JavaScript | declared |
292
+ | `@jarenjs/core/convert/currency` | JavaScript | declared |
293
+ | `@jarenjs/core/convert/index` | JavaScript | declared |
294
+ | `@jarenjs/core/convert/registry` | JavaScript | declared |
295
+ | `@jarenjs/core/package.json` | metadata | — |
296
+ <!--/fact-->
297
+
190
298
  ## Development
191
299
 
192
300
  Unit tests live in `test/core/` at the repository root (`npm run test:core`). This package's internals are described in its own [ARCHITECTURE](./ARCHITECTURE.md) document, with per-module references under [docs/](./docs/) (`MATH`, `CONVERT`, `FINANCE`, `DATES`, `GEO`); see the repository [README](../../README.md) and [ARCHITECTURE](../../docs/ARCHITECTURE.md) for the monorepo picture, and the [ROADMAP](../../docs/ROADMAP.md) for planned work.
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @file The bounded ordered asynchronous map: run a worker over a list
3
+ * with never more than `limit` calls in flight, and answer the results
4
+ * in the list's order. Before this file the same twelve lines lived in
5
+ * the AI package's program runner and in the benchmark harness, and a
6
+ * downstream consumer had written them a third time; a pool that exists
7
+ * once is one whose edge behavior can be pinned once.
8
+ *
9
+ * The contract, in full:
10
+ *
11
+ * - results are in INPUT order, whatever order the workers finish in;
12
+ * - never more than `limit` workers are in flight; `limit` must be a
13
+ * number of at least 1 (`Infinity` is allowed and means unbounded) —
14
+ * anything else is a `TypeError`, never a silent clamp, because a
15
+ * limit of 0 is a bug in the caller and "sequential" is spelled 1;
16
+ * - a worker rejection stops dispatch: no item starts after it, the
17
+ * workers already in flight are awaited, and only then does the map
18
+ * reject with that first rejection. A worker that throws
19
+ * synchronously is a rejection;
20
+ * - an abort does the same, rejecting with the signal's reason; a signal
21
+ * that is already aborted rejects before any worker runs;
22
+ * - so when the returned promise settles, NO worker is still running —
23
+ * the caller can close whatever the workers were using.
24
+ *
25
+ * The map does not retry, rate-limit, delay per origin or know anything
26
+ * about what the worker does; those are the caller's policies around it.
27
+ */
28
+ /**
29
+ * @template T, R
30
+ * @param {readonly T[]} items
31
+ * @param {number} limit - workers in flight at once; a number >= 1, `Infinity` for unbounded
32
+ * @param {(item: T, index: number) => Promise<R> | R} worker
33
+ * @param {{ signal?: AbortSignal }} [options]
34
+ * @returns {Promise<R[]>} the results, in input order
35
+ * @throws {TypeError} (as a rejection) when `limit` is not a number >= 1
36
+ */
37
+ export declare function mapConcurrent<T, R>(items: readonly T[], limit: number, worker: (item: T, index: number) => Promise<R> | R, options?: {
38
+ signal?: AbortSignal;
39
+ }): Promise<R[]>;
40
+ export type SinkLike<T> = {
41
+ write: (chunk: T) => unknown;
42
+ end?: () => unknown;
43
+ abort?: (reason: unknown) => unknown;
44
+ };
45
+ export type AwaitedSink<T> = {
46
+ /**
47
+ * - queue one
48
+ * chunk behind every earlier write; `undefined` when the underlying
49
+ * write answered synchronously with nothing pending (the fast path),
50
+ * else a promise that settles when the underlying write did
51
+ */
52
+ write: (chunk: T) => Promise<void> | undefined;
53
+ /**
54
+ * - queue the underlying `end` once
55
+ * after every write; repeated calls answer the same promise
56
+ */
57
+ end: () => Promise<void>;
58
+ /**
59
+ * - stop accepting
60
+ * writes, reject the writes still queued, and call the underlying
61
+ * `abort` once, immediately; repeated calls answer the same promise
62
+ */
63
+ abort: (reason?: unknown) => Promise<void>;
64
+ /**
65
+ * - whether a write rejected or an abort
66
+ * happened; nothing reaches the underlying sink after that
67
+ */
68
+ failed: () => boolean;
69
+ /**
70
+ * - whether `end` or `abort` was called
71
+ */
72
+ closed: () => boolean;
73
+ };
74
+ /**
75
+ * The underlying sink an awaited sink serializes: `write` answers a
76
+ * value (the chunk is written) or a promise (the chunk is written when
77
+ * it settles — a socket waiting for `drain`, a stream waiting for the
78
+ * consumer's next pull); `end` and `abort` are optional and may answer
79
+ * either way too.
80
+ * @template T
81
+ * @typedef {Object} SinkLike
82
+ * @property {(chunk: T) => unknown} write
83
+ * @property {() => unknown} [end]
84
+ * @property {(reason: unknown) => unknown} [abort]
85
+ */
86
+ /**
87
+ * The serialized, awaited view of a sink.
88
+ * @template T
89
+ * @typedef {Object} AwaitedSink
90
+ * @property {(chunk: T) => Promise<void> | undefined} write - queue one
91
+ * chunk behind every earlier write; `undefined` when the underlying
92
+ * write answered synchronously with nothing pending (the fast path),
93
+ * else a promise that settles when the underlying write did
94
+ * @property {() => Promise<void>} end - queue the underlying `end` once
95
+ * after every write; repeated calls answer the same promise
96
+ * @property {(reason?: unknown) => Promise<void>} abort - stop accepting
97
+ * writes, reject the writes still queued, and call the underlying
98
+ * `abort` once, immediately; repeated calls answer the same promise
99
+ * @property {() => boolean} failed - whether a write rejected or an abort
100
+ * happened; nothing reaches the underlying sink after that
101
+ * @property {() => boolean} closed - whether `end` or `abort` was called
102
+ */
103
+ /**
104
+ * Serialize a sink: every write waits for the previous one, `end` waits
105
+ * for every write, and one failure stops everything after it. This is
106
+ * the one place the suite decides what "the next chunk" means for a
107
+ * sink that may answer a promise — a Node response whose `write()` said
108
+ * `false` (wait for `drain`), a Web `ReadableStream` bridge that waits
109
+ * for the consumer's pull, a stream runner whose carrier hooks may be
110
+ * asynchronous — so backpressure is a property of the sink, never of
111
+ * the code writing into it.
112
+ *
113
+ * The contract, in full:
114
+ *
115
+ * - writes reach the underlying sink in call order and never overlap:
116
+ * the next underlying write begins only after the previous one
117
+ * settled;
118
+ * - a synchronous sink stays on a no-extra-promise fast path: while
119
+ * nothing is pending and the underlying write answers a non-thenable,
120
+ * `write` answers `undefined`; the first thenable answer opens the
121
+ * queue, and an idle queue returns to the fast path;
122
+ * - `end()` runs the underlying `end` once, after every write queued
123
+ * before it; `abort(reason)` runs the underlying `abort` once, at once
124
+ * (an abort is urgent — the pending underlying write is not waited
125
+ * for), and every write still queued rejects with the reason;
126
+ * - a write that throws or rejects (including a throwing `then` getter
127
+ * on its answer) fails the sink: its own promise
128
+ * rejects, every write queued behind it rejects with the same reason
129
+ * without reaching the underlying sink, later writes reject at once,
130
+ * and `end()` rejects too — the stream did not end cleanly;
131
+ * - `end` and `abort` are mutually terminal: the first one decides, the
132
+ * other one (and every repeat) answers the first one's promise; a
133
+ * write after either rejects;
134
+ * - `write` never throws — a refusal is a rejected promise, so a caller
135
+ * handles one shape.
136
+ *
137
+ * @template T
138
+ * @param {SinkLike<T>} sink
139
+ * @returns {AwaitedSink<T>}
140
+ * @throws {TypeError} when `sink` has no `write` function
141
+ */
142
+ export declare function createAwaitedSink<T>(sink: SinkLike<T>): AwaitedSink<T>;
@@ -45,7 +45,8 @@ export declare function compileDateFormat(pattern: string, names?: DateNames): (
45
45
  * `full-time`, and the offset is the record's own rather than UTC.
46
46
  *
47
47
  * The round trip preserves the *value*, not necessarily the spelling: a
48
- * fractional second is emitted only when non-zero and without trailing
48
+ * fractional second keeps the parser's six-digit precision and is emitted
49
+ * only when non-zero and without trailing
49
50
  * zeros, so `…:05.250Z` comes back `…:05.25Z`. The parts record holds
50
51
  * the fraction as a number, so the original digit count is not
51
52
  * recoverable — a consumer that must reproduce the input byte for byte
@@ -2,8 +2,9 @@
2
2
  * Deep equality comparison for arbitrary values.
3
3
  *
4
4
  * Generic JavaScript equality: understands Maps, Sets, RegExps,
5
- * functions, typed arrays and class instances (constructors must
6
- * match). Not the same as `equalsJson`, which compares JSON values
5
+ * functions, typed arrays and class instances (prototype constructors
6
+ * must match; an own `constructor` member is data). Not the same as
7
+ * `equalsJson`, which compares JSON values
7
8
  * only and is the hot-path variant — keep both.
8
9
  * @param {any} target
9
10
  * @param {any} source
@@ -111,7 +112,7 @@ export declare function contentKey(value: any): string;
111
112
  * functions, symbols, cycles, and non-plain objects (a `Date`, `Map`,
112
113
  * `RegExp` or class instance, all of which serialize to `{}`), plus the
113
114
  * two members a serialization cannot show — a symbol key, and an own
114
- * array property past the last element. A caller that may hold such a
115
+ * array property that is not an element index. A caller that may hold such a
115
116
  * value must treat the refusal as "not cacheable" and compute afresh —
116
117
  * never as "reuse whatever shares the key".
117
118
  *
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @file The suite's one seeded generator, and the three draws built on
3
+ * it. Every seeded corpus, oracle and property test in this repository
4
+ * needs the same thing: a stream of numbers that is identical on every
5
+ * host for a given seed, so that a benchmark can state a delta and a
6
+ * failing property test can be replayed. Before this file that stream
7
+ * was written ten times; a generator that exists once is one whose
8
+ * sequence can be pinned once.
9
+ *
10
+ * The algorithm is mulberry32 — a 32-bit state, one multiply-xorshift
11
+ * round per draw, a period of 2^32. It is named by its algorithm rather
12
+ * than by its role because the SEQUENCE is the contract: a corpus
13
+ * generated from seed 20260825 must regenerate byte-for-byte, and a
14
+ * "better" generator under the same name would silently change every
15
+ * fixture that trusts it. A second algorithm gets a second name.
16
+ *
17
+ * Nothing here is cryptographic, and nothing here reads `Math.random`.
18
+ */
19
+ /**
20
+ * A seeded generator: uniform in `[0, 1)`, identical on every host for
21
+ * the same seed.
22
+ *
23
+ * The seed is taken as an unsigned 32-bit integer (`seed >>> 0`, the
24
+ * ToUint32 conversion): `1.5` seeds as `1`, `-1` as `4294967295`,
25
+ * `2^32 + 5` as `5`, and `NaN` as `0`. Two seeds that agree modulo 2^32
26
+ * are one stream — say so wherever a seed is published.
27
+ * @param {number} seed
28
+ * @returns {() => number} the stream; each call is the next draw
29
+ */
30
+ export declare function mulberry32(seed: number): () => number;
31
+ /**
32
+ * One integer draw over the half-open range `[min, max)`: `min +
33
+ * floor(random() * (max - min))`.
34
+ *
35
+ * This is the floor draw and deliberately so. Every committed corpus in
36
+ * the suite was generated with exactly this arithmetic, and a generator
37
+ * whose integer draw changed would regenerate every one of them
38
+ * differently. It is uniform up to a bias bounded by `(max - min) /
39
+ * 2^32` — under one part in a million for a span of four thousand, and
40
+ * far below anything a benchmark row can resolve. A draw that rejects
41
+ * to remove even that bias would be a different function under a
42
+ * different name, not a change to this one.
43
+ * @param {() => number} random - the stream, from {@link mulberry32}
44
+ * @param {number} min - inclusive integer lower bound
45
+ * @param {number} max - exclusive integer upper bound; must exceed `min`
46
+ * @returns {number} an integer in `[min, max)`
47
+ * @throws {RangeError} when a bound is not an integer or `max <= min`
48
+ */
49
+ export declare function randomInt(random: () => number, min: number, max: number): number;
50
+ /**
51
+ * Fisher–Yates, in place, from the given stream: for `i` from the last
52
+ * index down to 1, swap `i` with a uniform `j` in `[0, i]`. Returns the
53
+ * same array. An empty or one-element list draws nothing.
54
+ *
55
+ * The descending form is the one the seeded corpora were generated
56
+ * with; the ascending form is a different permutation of the same
57
+ * stream and must not be substituted.
58
+ * @template T
59
+ * @param {() => number} random - the stream, from {@link mulberry32}
60
+ * @param {T[]} list - reordered in place
61
+ * @returns {T[]} `list`
62
+ */
63
+ export declare function shuffle<T>(random: () => number, list: T[]): T[];
64
+ /**
65
+ * `k` distinct indices from `[0, n)`, uniformly, as a partial forward
66
+ * Fisher–Yates over a fresh index pool: the first `k` positions of a
67
+ * shuffle, without paying for the rest. Asked for more than `n` it
68
+ * answers `n` — a draw cannot invent a member the population does not
69
+ * hold; asked for nothing, or from nothing, it answers `[]`.
70
+ *
71
+ * The stream is the caller's, so one stream can serve many draws
72
+ * (a policy that draws per question from one seeded closure stays
73
+ * reproducible across the whole run).
74
+ * @param {() => number} random - the stream, from {@link mulberry32}
75
+ * @param {number} n - the population size (a non-negative integer)
76
+ * @param {number} k - how many to draw (a non-negative integer)
77
+ * @returns {number[]} `min(k, n)` distinct indices, in draw order
78
+ * @throws {RangeError} when `n` or `k` is not a non-negative integer
79
+ */
80
+ export declare function drawDistinct(random: () => number, n: number, k: number): number[];
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @file The runtime record: the host facts every subsystem that needs
3
+ * one used to take separately — the clock, secure identifiers,
4
+ * randomness and the zone provider — as one frozen record a host builds
5
+ * once and hands to the store, the jobs engine, the migration runner and
6
+ * the http binding. Nothing here IS a clock, a random source or a zone
7
+ * database: the defaults are the platform's own (`Date.now`,
8
+ * `crypto.randomUUID`, `Math.random`, and no zone provider, which keeps
9
+ * a named zone the refusal it always was), so adopting the record changes
10
+ * nothing a consumer can observe. What it buys is that a deterministic
11
+ * run — a fixed clock, a seeded generator, a counting identifier — is
12
+ * configured in one place, and that four subsystems can no longer
13
+ * disagree about what time it is.
14
+ *
15
+ * Precedence is fixed: a subsystem's own explicit option wins over the
16
+ * record's member, which wins over the built-in default. The options are
17
+ * published surface, and a record that silently overrode them would be a
18
+ * breaking change dressed as an ergonomic.
19
+ *
20
+ * The record reaches hosts, never query compilation. A compiled query is
21
+ * cached by document identity and saved as a rule, so the current instant
22
+ * enters it as data — an external — and there is no `now` operator for
23
+ * this record to feed.
24
+ */
25
+ export type Runtime = {
26
+ /**
27
+ * - the clock, in epoch milliseconds
28
+ */
29
+ now: () => number;
30
+ /**
31
+ * - a fresh identifier; secure by default
32
+ */
33
+ uuid: () => string;
34
+ /**
35
+ * - uniform in `[0, 1)`
36
+ */
37
+ random: () => number;
38
+ /**
39
+ * - the tzdb a named zone is read through, or `null`: a named zone is
40
+ * then a refusal, never a quiet UTC
41
+ */
42
+ zoneProvider: import('./series/zone.js').ZoneProvider | null;
43
+ };
44
+ /**
45
+ * The record every host-facing subsystem takes as `runtime`.
46
+ * @typedef {Object} Runtime
47
+ * @property {() => number} now - the clock, in epoch milliseconds
48
+ * @property {() => string} uuid - a fresh identifier; secure by default
49
+ * @property {() => number} random - uniform in `[0, 1)`
50
+ * @property {import('./series/zone.js').ZoneProvider | null} zoneProvider
51
+ * - the tzdb a named zone is read through, or `null`: a named zone is
52
+ * then a refusal, never a quiet UTC
53
+ */
54
+ /**
55
+ * The four members a runtime record carries, so a subsystem, a test and
56
+ * a document all spell the host facts the same way.
57
+ */
58
+ export declare const RUNTIME_MEMBERS: readonly string[];
59
+ /**
60
+ * Build a runtime record: the platform defaults, with any member
61
+ * overridden. The result is frozen, so a subsystem that was handed one
62
+ * can hand it on without a copy.
63
+ *
64
+ * The record is closed: a member it does not have is a refusal naming
65
+ * the four it does, because `clock` for `now` quietly ignored would be a
66
+ * deterministic run that is not.
67
+ *
68
+ * @param {Partial<Runtime>} [overrides]
69
+ * @returns {Readonly<Runtime>}
70
+ * @throws {TypeError} for a member that is not a function, a
71
+ * `zoneProvider` that is neither `null` nor a provider, or a member
72
+ * the record does not have
73
+ * @example
74
+ * createRuntime(); // the platform's own
75
+ * createRuntime({ now: () => 1_700_000_000_000 }); // a fixed clock, the rest default
76
+ * createRuntime({ uuid: () => `id-${++n}`, random: mulberry32(1), zoneProvider });
77
+ */
78
+ export declare function createRuntime(overrides?: Partial<Runtime>): Readonly<Runtime>;
79
+ /**
80
+ * The record a subsystem reads its `runtime` option through: nothing
81
+ * given is the platform default, and anything given is validated and
82
+ * frozen — so every member a subsystem reads is a function, and a
83
+ * malformed record is refused where it was passed rather than where it
84
+ * was first called.
85
+ * @param {Partial<Runtime> | undefined | null} [candidate] - a subsystem's `options.runtime`
86
+ * @returns {Readonly<Runtime>}
87
+ * @throws {TypeError} as `createRuntime` does
88
+ */
89
+ export declare function resolveRuntime(candidate?: Partial<Runtime> | undefined | null): Readonly<Runtime>;
@@ -2,6 +2,13 @@ export { toEpoch, normalizeSeries, canonicalSeries, normalizeIntervals, lowerBou
2
2
  export { containsInstant, overlapsInterval, intersectInterval, mergeIntervals, subtractIntervals, gapsWithin, coverageOf, findSlots, MERGE_MEMBERS, SLOTS_MEMBERS, } from './interval.js';
3
3
  export { createIntervalIndex } from './interval-index.js';
4
4
  export { resolveClock, CLOCK_MEMBERS } from './zone.js';
5
+ export type ZoneProvider = import('./zone.js').ZoneProvider;
6
+ /**
7
+ * The wall clock a caller supplies for a named zone - the shape a host
8
+ * passes as `provider`, named here so it can be spelled where the seam
9
+ * is crossed.
10
+ * @typedef {import('./zone.js').ZoneProvider} ZoneProvider
11
+ */
5
12
  export { compileBuckets, resampleSeries, RESAMPLE_MEMBERS } from './bucket.js';
6
13
  export { rollingSeries, ROLLING_MEMBERS } from './rolling.js';
7
14
  export { asOfJoin, ASOF_MEMBERS } from './asof.js';