@saber-usa/node-common 1.7.18-alpha.1 → 1.7.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,147 +1,147 @@
1
- import {createSingleThreadRuntime, BulkPropagator} from "satellite.js";
2
-
3
- // Module-local state. In Node, ES module instances are per Realm/per worker
4
- // thread, so this state is naturally per-thread when consumed inside
5
- // `worker_threads` (e.g. node-pub-sub's workerpool workers).
6
- //
7
- // See docs/BULK_PROPAGATION.md sections 6, 7, and 8 for the consumer model,
8
- // lifecycle, and registry design.
9
- let runtimePromise = null;
10
- let registry = new Map();
11
-
12
- /**
13
- * Lazily creates (or returns) the per-thread WASM runtime.
14
- *
15
- * On first call within a thread, triggers `createSingleThreadRuntime()`
16
- * (the v7 successor to the blog's `createWasmModule()`) and caches the
17
- * resulting promise. All subsequent callers in that thread reuse it.
18
- *
19
- * Safe to await concurrently: every caller awaits the same in-flight
20
- * promise, so only one WASM compile + instantiate ever happens.
21
- *
22
- * @return {Promise<import("satellite.js").SingleThreadRuntime>}
23
- */
24
- const getBulkRuntime = () => {
25
- if (runtimePromise === null) {
26
- runtimePromise = createSingleThreadRuntime();
27
- }
28
- return runtimePromise;
29
- };
30
-
31
- /**
32
- * Eager bootstrap. Equivalent to `await getBulkRuntime()` but named for the
33
- * explicit warm-up call site (e.g. top of `dedicated_worker.js`).
34
- *
35
- * @return {Promise<import("satellite.js").SingleThreadRuntime>}
36
- */
37
- const initBulkRuntime = () => getBulkRuntime();
38
-
39
- /**
40
- * Returns true if the runtime has been requested in this thread (regardless
41
- * of whether it has finished initializing). Used by tests and by the
42
- * disposal path to decide whether dispose work is necessary.
43
- *
44
- * @return {boolean}
45
- */
46
- const isBulkRuntimeInitialized = () => runtimePromise !== null;
47
-
48
- /**
49
- * Acquires (or lazily constructs) the per-thread, per-pipeline singleton
50
- * `BulkPropagator` for the given pipeline key.
51
- *
52
- * Reuse rules:
53
- * - The same instance is returned on every call with the same `key`.
54
- * - If the caller's `satRecsCount` or `datesCount` exceed the current
55
- * allocation, the propagator's own `setSatRecs` / `setDates` will perform
56
- * a single `free` + `malloc` grow-realloc internally on the next set.
57
- * The initial allocation here is sized to the first caller's request, so
58
- * "warm" subsequent calls of the same shape pay zero allocation cost.
59
- *
60
- * Caller responsibilities:
61
- * - `setSatRecs`, `setDates`, then `run()` between `acquire` and using
62
- * results. Within a single thread these are synchronous so no other
63
- * pipeline consumer can interleave (per docs/BULK_PROPAGATION.md §9).
64
- * - **Do not call `dispose()` on the returned propagator.** Lifecycle is
65
- * owned by the registry; `disposeBulkRuntime()` is the only sanctioned
66
- * teardown path.
67
- *
68
- * @param {Object} params
69
- * @param {import("satellite.js").SingleThreadRuntime} params.runtime
70
- * @param {string} params.key Pipeline signature, e.g. `"eci"`.
71
- * @param {() => readonly any[]} params.makeCalculators Factory invoked once
72
- * per (key, thread) to build fresh calculator instances. A factory is
73
- * required because calculator instances are stateful and bound to a
74
- * propagator at construction time; they cannot be shared across keys.
75
- * @param {number} params.satRecsCount Initial satellite-record allocation
76
- * hint. Used only on first construction for this key in this thread.
77
- * @param {number} params.datesCount Initial dates allocation hint. Same
78
- * first-call-only semantics.
79
- * @return {import("satellite.js").BulkPropagator<any, any>}
80
- */
81
- const getOrCreateBulkPropagator = ({
82
- runtime,
83
- key,
84
- makeCalculators,
85
- satRecsCount,
86
- datesCount,
87
- }) => {
88
- const cached = registry.get(key);
89
- if (cached) {
90
- return cached;
91
- }
92
-
93
- const propagator = new BulkPropagator({
94
- runtime,
95
- calculators: makeCalculators(),
96
- satRecsCount: Math.max(1, satRecsCount),
97
- datesCount: Math.max(1, datesCount),
98
- });
99
- registry.set(key, propagator);
100
- return propagator;
101
- };
102
-
103
- /**
104
- * Releases all WASM memory owned by this thread: every registry-held
105
- * `BulkPropagator` is disposed and the runtime itself is torn down.
106
- *
107
- * Idempotent: safe to call when nothing was ever initialized. After this
108
- * returns, the next `getBulkRuntime()` will lazily re-initialize from
109
- * scratch.
110
- */
111
- const disposeBulkRuntime = async () => {
112
- for (const propagator of registry.values()) {
113
- try {
114
- propagator.dispose();
115
- } catch (e) {
116
- // Disposal must never throw out of teardown; we surface the
117
- // failure to stderr but continue cleaning up the rest of the
118
- // registry to minimize the leak surface.
119
- console.error("disposeBulkRuntime: failed to dispose propagator", e);
120
- }
121
- }
122
- registry = new Map();
123
-
124
- if (runtimePromise !== null) {
125
- const pending = runtimePromise;
126
- runtimePromise = null;
127
- try {
128
- const runtime = await pending;
129
- runtime.dispose();
130
- } catch (e) {
131
- // Emscripten's `_exit_runtime` (with EXIT_RUNTIME=1) signals clean
132
- // shutdown by throwing an `ExitStatus` object with `status === 0`.
133
- // That is the documented success path, not a failure to log.
134
- if (e?.name !== "ExitStatus" || e?.status !== 0) {
135
- console.error("disposeBulkRuntime: failed to dispose runtime", e);
136
- }
137
- }
138
- }
139
- };
140
-
141
- export {
142
- getBulkRuntime,
143
- initBulkRuntime,
144
- isBulkRuntimeInitialized,
145
- getOrCreateBulkPropagator,
146
- disposeBulkRuntime,
147
- };
1
+ import {createSingleThreadRuntime, BulkPropagator} from "satellite.js";
2
+
3
+ // Module-local state. In Node, ES module instances are per Realm/per worker
4
+ // thread, so this state is naturally per-thread when consumed inside
5
+ // `worker_threads` (e.g. node-pub-sub's workerpool workers).
6
+ //
7
+ // See docs/BULK_PROPAGATION.md sections 6, 7, and 8 for the consumer model,
8
+ // lifecycle, and registry design.
9
+ let runtimePromise = null;
10
+ let registry = new Map();
11
+
12
+ /**
13
+ * Lazily creates (or returns) the per-thread WASM runtime.
14
+ *
15
+ * On first call within a thread, triggers `createSingleThreadRuntime()`
16
+ * (the v7 successor to the blog's `createWasmModule()`) and caches the
17
+ * resulting promise. All subsequent callers in that thread reuse it.
18
+ *
19
+ * Safe to await concurrently: every caller awaits the same in-flight
20
+ * promise, so only one WASM compile + instantiate ever happens.
21
+ *
22
+ * @return {Promise<import("satellite.js").SingleThreadRuntime>}
23
+ */
24
+ const getBulkRuntime = () => {
25
+ if (runtimePromise === null) {
26
+ runtimePromise = createSingleThreadRuntime();
27
+ }
28
+ return runtimePromise;
29
+ };
30
+
31
+ /**
32
+ * Eager bootstrap. Equivalent to `await getBulkRuntime()` but named for the
33
+ * explicit warm-up call site (e.g. top of `dedicated_worker.js`).
34
+ *
35
+ * @return {Promise<import("satellite.js").SingleThreadRuntime>}
36
+ */
37
+ const initBulkRuntime = () => getBulkRuntime();
38
+
39
+ /**
40
+ * Returns true if the runtime has been requested in this thread (regardless
41
+ * of whether it has finished initializing). Used by tests and by the
42
+ * disposal path to decide whether dispose work is necessary.
43
+ *
44
+ * @return {boolean}
45
+ */
46
+ const isBulkRuntimeInitialized = () => runtimePromise !== null;
47
+
48
+ /**
49
+ * Acquires (or lazily constructs) the per-thread, per-pipeline singleton
50
+ * `BulkPropagator` for the given pipeline key.
51
+ *
52
+ * Reuse rules:
53
+ * - The same instance is returned on every call with the same `key`.
54
+ * - If the caller's `satRecsCount` or `datesCount` exceed the current
55
+ * allocation, the propagator's own `setSatRecs` / `setDates` will perform
56
+ * a single `free` + `malloc` grow-realloc internally on the next set.
57
+ * The initial allocation here is sized to the first caller's request, so
58
+ * "warm" subsequent calls of the same shape pay zero allocation cost.
59
+ *
60
+ * Caller responsibilities:
61
+ * - `setSatRecs`, `setDates`, then `run()` between `acquire` and using
62
+ * results. Within a single thread these are synchronous so no other
63
+ * pipeline consumer can interleave (per docs/BULK_PROPAGATION.md §9).
64
+ * - **Do not call `dispose()` on the returned propagator.** Lifecycle is
65
+ * owned by the registry; `disposeBulkRuntime()` is the only sanctioned
66
+ * teardown path.
67
+ *
68
+ * @param {Object} params
69
+ * @param {import("satellite.js").SingleThreadRuntime} params.runtime
70
+ * @param {string} params.key Pipeline signature, e.g. `"eci"`.
71
+ * @param {() => readonly any[]} params.makeCalculators Factory invoked once
72
+ * per (key, thread) to build fresh calculator instances. A factory is
73
+ * required because calculator instances are stateful and bound to a
74
+ * propagator at construction time; they cannot be shared across keys.
75
+ * @param {number} params.satRecsCount Initial satellite-record allocation
76
+ * hint. Used only on first construction for this key in this thread.
77
+ * @param {number} params.datesCount Initial dates allocation hint. Same
78
+ * first-call-only semantics.
79
+ * @return {import("satellite.js").BulkPropagator<any, any>}
80
+ */
81
+ const getOrCreateBulkPropagator = ({
82
+ runtime,
83
+ key,
84
+ makeCalculators,
85
+ satRecsCount,
86
+ datesCount,
87
+ }) => {
88
+ const cached = registry.get(key);
89
+ if (cached) {
90
+ return cached;
91
+ }
92
+
93
+ const propagator = new BulkPropagator({
94
+ runtime,
95
+ calculators: makeCalculators(),
96
+ satRecsCount: Math.max(1, satRecsCount),
97
+ datesCount: Math.max(1, datesCount),
98
+ });
99
+ registry.set(key, propagator);
100
+ return propagator;
101
+ };
102
+
103
+ /**
104
+ * Releases all WASM memory owned by this thread: every registry-held
105
+ * `BulkPropagator` is disposed and the runtime itself is torn down.
106
+ *
107
+ * Idempotent: safe to call when nothing was ever initialized. After this
108
+ * returns, the next `getBulkRuntime()` will lazily re-initialize from
109
+ * scratch.
110
+ */
111
+ const disposeBulkRuntime = async () => {
112
+ for (const propagator of registry.values()) {
113
+ try {
114
+ propagator.dispose();
115
+ } catch (e) {
116
+ // Disposal must never throw out of teardown; we surface the
117
+ // failure to stderr but continue cleaning up the rest of the
118
+ // registry to minimize the leak surface.
119
+ console.error("disposeBulkRuntime: failed to dispose propagator", e);
120
+ }
121
+ }
122
+ registry = new Map();
123
+
124
+ if (runtimePromise !== null) {
125
+ const pending = runtimePromise;
126
+ runtimePromise = null;
127
+ try {
128
+ const runtime = await pending;
129
+ runtime.dispose();
130
+ } catch (e) {
131
+ // Emscripten's `_exit_runtime` (with EXIT_RUNTIME=1) signals clean
132
+ // shutdown by throwing an `ExitStatus` object with `status === 0`.
133
+ // That is the documented success path, not a failure to log.
134
+ if (e?.name !== "ExitStatus" || e?.status !== 0) {
135
+ console.error("disposeBulkRuntime: failed to dispose runtime", e);
136
+ }
137
+ }
138
+ }
139
+ };
140
+
141
+ export {
142
+ getBulkRuntime,
143
+ initBulkRuntime,
144
+ isBulkRuntimeInitialized,
145
+ getOrCreateBulkPropagator,
146
+ disposeBulkRuntime,
147
+ };