@c9up/eon 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EonProvider.d.ts +10 -0
- package/dist/EonProvider.d.ts.map +1 -1
- package/dist/EonProvider.js +167 -24
- package/dist/EonProvider.js.map +1 -1
- package/dist/query/native.d.ts.map +1 -1
- package/dist/query/native.js +11 -27
- package/dist/query/native.js.map +1 -1
- package/dist/vendor/nativeBinary.d.ts +43 -0
- package/dist/vendor/nativeBinary.d.ts.map +1 -0
- package/dist/vendor/nativeBinary.js +99 -0
- package/dist/vendor/nativeBinary.js.map +1 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +3 -2
- package/src/EonProvider.ts +201 -25
- package/src/query/native.ts +18 -31
- package/src/vendor/nativeBinary.ts +110 -0
package/dist/EonProvider.d.ts
CHANGED
|
@@ -34,6 +34,16 @@ export interface EonAppContext {
|
|
|
34
34
|
config: {
|
|
35
35
|
get<T = unknown>(key: string): T | undefined;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Optional reader — present on ream's real application.
|
|
39
|
+
*
|
|
40
|
+
* `'warmup'` means the application was assembled to be INSPECTED rather than
|
|
41
|
+
* run: a route listing, a codegen pass, a config dump. Providers register,
|
|
42
|
+
* boot and start on that path too, so anything that opens a socket or
|
|
43
|
+
* creates a database has to ask first. A host that does not implement it is
|
|
44
|
+
* treated as running.
|
|
45
|
+
*/
|
|
46
|
+
getMode?(): string;
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
39
49
|
* The transport-independent compiler service registered under `eon.compiler`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EonProvider.d.ts","sourceRoot":"","sources":["../src/EonProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EACN,KAAK,iBAAiB,EAEtB,KAAK,UAAU,EACf,MAAM,mBAAmB,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE;QACV,SAAS,CACR,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,EAC5D,OAAO,EAAE,MAAM,OAAO,GACpB,IAAI,CAAC;QACR;;;;WAIG;QACH,OAAO,CAAC,CACP,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAC1D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC9B,CAAC;IACF,MAAM,EAAE;QAAE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"EonProvider.d.ts","sourceRoot":"","sources":["../src/EonProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EACN,KAAK,iBAAiB,EAEtB,KAAK,UAAU,EACf,MAAM,mBAAmB,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE;QACV,SAAS,CACR,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,EAC5D,OAAO,EAAE,MAAM,OAAO,GACpB,IAAI,CAAC;QACR;;;;WAIG;QACH,OAAO,CAAC,CACP,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAC1D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC9B,CAAC;IACF,MAAM,EAAE;QAAE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;IACzD;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,iBAAiB,CAAC;CAC/D;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAC1B,MAAM,EAAE,mBAAmB,KACvB,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5B,qBAAa,WAAW;;gBAUX,GAAG,EAAE,aAAa,EAAE,OAAO,GAAE,YAA2B;IAKpE,QAAQ,IAAI,IAAI;IAgFV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuNrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAkF/B;AAID,eAAe,WAAW,CAAC"}
|
package/dist/EonProvider.js
CHANGED
|
@@ -22,20 +22,90 @@ export class EonProvider {
|
|
|
22
22
|
#connections = new Map();
|
|
23
23
|
/** Set once a full boot opened connections — guards against a leaking re-boot. */
|
|
24
24
|
#booted = false;
|
|
25
|
+
/** What the `eon` / `eon.connection` tokens resolve to, once boot opened it. */
|
|
26
|
+
#defaultConnection;
|
|
25
27
|
constructor(app, connect = connectWsEon) {
|
|
26
28
|
this.#app = app;
|
|
27
29
|
this.#connect = connect;
|
|
28
30
|
}
|
|
29
|
-
register() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
31
|
+
register() {
|
|
32
|
+
// Bindings belong here, not in boot(). Upstream registers in `register`
|
|
33
|
+
// precisely so another provider can count on them during its own boot;
|
|
34
|
+
// bound in boot, the container had a different surface depending on
|
|
35
|
+
// which provider ran first, and `eon.compiler` was simply unavailable to
|
|
36
|
+
// anything booting earlier.
|
|
37
|
+
//
|
|
38
|
+
// It is also pure — a compile is a call into the native compiler, no
|
|
39
|
+
// socket, no database — so there is nothing here an inspection must
|
|
40
|
+
// avoid.
|
|
35
41
|
const service = {
|
|
36
42
|
compile: (spec, dialect) => compileStatementNative(spec, dialect),
|
|
37
43
|
};
|
|
38
44
|
this.#app.container.singleton("eon.compiler", () => service);
|
|
45
|
+
// The connection tokens are registered here too, so the surface does not
|
|
46
|
+
// change between warmup and run. What they RESOLVE to still depends on
|
|
47
|
+
// boot having opened something — a lazy factory cannot await — so
|
|
48
|
+
// reading one too early says what to do instead of answering undefined.
|
|
49
|
+
this.#app.container.singleton("eon", () => this.#requireDefault("eon"));
|
|
50
|
+
this.#app.container.singleton("eon.connection", () => this.#requireDefault("eon.connection"));
|
|
51
|
+
// The NAMED tokens too. Their names come from the config, which is
|
|
52
|
+
// readable here — nothing about `eon:primary` needs a socket to exist.
|
|
53
|
+
// Bound in boot they appeared only on the run path, so the container had
|
|
54
|
+
// a different surface in an inspection, and a boot that failed late left
|
|
55
|
+
// factories behind pointing at connections it had just closed.
|
|
56
|
+
const config = this.#app.config.get("timeseries") ??
|
|
57
|
+
this.#app.config.get("eon");
|
|
58
|
+
if (!config)
|
|
59
|
+
return;
|
|
60
|
+
for (const name of Object.keys(this.#resolveConnections(config).connections)) {
|
|
61
|
+
this.#app.container.singleton(`eon:${name}`, () => this.#requireNamed(name));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A named connection, or a message saying what is missing.
|
|
66
|
+
*
|
|
67
|
+
* Same reasoning as {@link #requireDefault}: resolving to `undefined` sent
|
|
68
|
+
* the caller into a TypeError several frames from the cause.
|
|
69
|
+
*/
|
|
70
|
+
#requireNamed(name) {
|
|
71
|
+
const connection = this.#connections.get(name);
|
|
72
|
+
if (connection === undefined) {
|
|
73
|
+
throw new Error(`EonProvider: connection '${name}' was resolved before it was opened. Connections open in boot(); check that config.timeseries.connections defines '${name}' and that EonProvider is listed before whatever resolved this.`);
|
|
74
|
+
}
|
|
75
|
+
return connection;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The default connection, or a message naming what is missing.
|
|
79
|
+
*
|
|
80
|
+
* Resolving to `undefined` sent the caller into a `TypeError` several
|
|
81
|
+
* frames away from the cause — a provider that resolved eon during its own
|
|
82
|
+
* boot, or an application with no `config/timeseries.ts` at all.
|
|
83
|
+
*/
|
|
84
|
+
#requireDefault(token) {
|
|
85
|
+
const connection = this.#defaultConnection;
|
|
86
|
+
if (connection === undefined) {
|
|
87
|
+
throw new Error(`EonProvider: '${token}' was resolved before any connection was opened. Connections open in boot(); check that config.timeseries defines one and that EonProvider is listed before whatever resolved this.`);
|
|
88
|
+
}
|
|
89
|
+
return connection;
|
|
90
|
+
}
|
|
91
|
+
/** True when the application was assembled to be inspected rather than run. */
|
|
92
|
+
#isInspecting() {
|
|
93
|
+
return this.#app.getMode?.() === "warmup";
|
|
94
|
+
}
|
|
95
|
+
async boot() {
|
|
96
|
+
// An inspection opens no WebSocket and creates no database. `warmUp()`
|
|
97
|
+
// runs boot, so a route listing reached TDengine — and could CREATE a
|
|
98
|
+
// database there — while `shutdown()` never fires on that path, leaving
|
|
99
|
+
// the sockets open. The compiler binding below is pure and stays.
|
|
100
|
+
if (this.#booted) {
|
|
101
|
+
throw new Error("EonProvider: boot() has already opened connections; a second boot would overwrite and leak them. Call shutdown() first, or construct a new provider.");
|
|
102
|
+
}
|
|
103
|
+
// Everything here opens a socket or creates a database, so an
|
|
104
|
+
// inspection stops at the compiler. `warmUp()` runs boot, so a route
|
|
105
|
+
// listing reached TDengine — and could CREATE a database there — while
|
|
106
|
+
// `shutdown()` never fires on that path, leaving the sockets open.
|
|
107
|
+
if (this.#isInspecting())
|
|
108
|
+
return;
|
|
39
109
|
const config = this.#app.config.get("timeseries") ??
|
|
40
110
|
this.#app.config.get("eon");
|
|
41
111
|
if (!config)
|
|
@@ -83,22 +153,63 @@ export class EonProvider {
|
|
|
83
153
|
this.#connections.clear();
|
|
84
154
|
throw new Error(`EonProvider: default connection '${defaultName}' is not defined in config.timeseries.connections`);
|
|
85
155
|
}
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
156
|
+
// Everything past this point runs with the sockets ALREADY OPEN, and any
|
|
157
|
+
// of it can fail: importing the services module, registering the
|
|
158
|
+
// migration source. Without this the failure left the connections open,
|
|
159
|
+
// possibly the module singleton published, and `#booted` false — so the
|
|
160
|
+
// next attempt opened another set on top of the ones nobody could reach.
|
|
161
|
+
try {
|
|
162
|
+
// The tokens were all bound in `register()`; this is what they
|
|
163
|
+
// resolve to. Filling the map is what makes them answer.
|
|
164
|
+
this.#defaultConnection = defaultConn;
|
|
165
|
+
// Populate the `@c9up/eon/services/connection` singleton so apps can
|
|
166
|
+
// `import connection from '@c9up/eon/services/connection'` anywhere.
|
|
167
|
+
// Lazy import so a type-only discovery scan does not pull the module
|
|
168
|
+
// at construction time.
|
|
169
|
+
const { setConnection } = await import("./services/connection.js");
|
|
170
|
+
setConnection(defaultConn);
|
|
171
|
+
await this.#registerMigrationSource(defaultConn, config);
|
|
172
|
+
this.#booted = true;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
await this.#rollbackBoot(defaultConn);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Undo a boot that opened its connections and then failed.
|
|
181
|
+
*
|
|
182
|
+
* Everything this releases was created by THIS attempt, so a retry starts
|
|
183
|
+
* from nothing rather than from a half-assembled state. The module
|
|
184
|
+
* singleton is released only while it is still the one we published — two
|
|
185
|
+
* applications can share a process, and the survivor's connection must
|
|
186
|
+
* stay.
|
|
187
|
+
*/
|
|
188
|
+
async #rollbackBoot(published) {
|
|
189
|
+
await this.#releaseMigrationSource();
|
|
190
|
+
// The connector keeps PROCESS-GLOBAL handles, so closing the individual
|
|
191
|
+
// connections is not enough to let Node exit — shutdown() says so and
|
|
192
|
+
// destroys them; a failed boot left them behind and the process hung.
|
|
193
|
+
const hadWebsocket = [...this.#connections.values()].some((conn) => conn.transport === "websocket");
|
|
194
|
+
try {
|
|
195
|
+
const { clearConnection } = await import("./services/connection.js");
|
|
196
|
+
clearConnection(published);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// The module could not be loaded, so nothing was published from it.
|
|
200
|
+
}
|
|
201
|
+
this.#defaultConnection = undefined;
|
|
202
|
+
await Promise.allSettled([...this.#connections.values()].map((conn) => conn.close()));
|
|
203
|
+
this.#connections.clear();
|
|
204
|
+
if (hadWebsocket) {
|
|
205
|
+
try {
|
|
206
|
+
const { destroyEonConnector } = await import("./connection/websocket.js");
|
|
207
|
+
await destroyEonConnector();
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// A fake-only boot never pulled the connector in.
|
|
211
|
+
}
|
|
212
|
+
}
|
|
102
213
|
}
|
|
103
214
|
/**
|
|
104
215
|
* Hand eon's migration runner to the framework's `migrations` registry, so
|
|
@@ -128,20 +239,52 @@ export class EonProvider {
|
|
|
128
239
|
}
|
|
129
240
|
const { EonMigrationRunner } = await import("./schema/EonMigrationRunner.js");
|
|
130
241
|
const migrationsDir = config.migrationsDir ?? "database/eon-migrations";
|
|
131
|
-
|
|
242
|
+
const source = {
|
|
132
243
|
name: "eon",
|
|
133
244
|
directory: migrationsDir,
|
|
134
245
|
runner: new EonMigrationRunner(conn, { migrationsDir }),
|
|
135
|
-
}
|
|
246
|
+
};
|
|
247
|
+
registry.register(source);
|
|
248
|
+
// Kept so shutdown can hand the name back. Registering refuses a
|
|
249
|
+
// duplicate, so a provider that stops without releasing its name leaves
|
|
250
|
+
// a second boot in the same process failing on "already registered" —
|
|
251
|
+
// and the CLI driving a runner that holds a closed connection.
|
|
252
|
+
this.#migrationSource = source;
|
|
253
|
+
}
|
|
254
|
+
/** What this provider registered with the host's migration registry. */
|
|
255
|
+
#migrationSource;
|
|
256
|
+
/** Give the migration name back, while it is still ours. */
|
|
257
|
+
async #releaseMigrationSource() {
|
|
258
|
+
const source = this.#migrationSource;
|
|
259
|
+
if (source === undefined)
|
|
260
|
+
return;
|
|
261
|
+
this.#migrationSource = undefined;
|
|
262
|
+
const resolve = this.#app.container.resolve;
|
|
263
|
+
if (typeof resolve !== "function")
|
|
264
|
+
return;
|
|
265
|
+
try {
|
|
266
|
+
const registry = await resolve.call(this.#app.container, "migrations");
|
|
267
|
+
if (typeof registry === "object" &&
|
|
268
|
+
registry !== null &&
|
|
269
|
+
typeof Reflect.get(registry, "unregister") === "function") {
|
|
270
|
+
registry.unregister("eon", source);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// No registry, or an older host without `unregister`: nothing to give
|
|
275
|
+
// back, and a shutdown must not fail over it.
|
|
276
|
+
}
|
|
136
277
|
}
|
|
137
278
|
async shutdown() {
|
|
138
279
|
// Close every connection in parallel. `allSettled` (fail-open) so a single
|
|
139
280
|
// stuck close doesn't block the rest, but failures are aggregated and
|
|
140
281
|
// rethrown so supervisors see a non-zero shutdown signal. The map + module
|
|
141
282
|
// singleton are cleared unconditionally (never hand out closed handles).
|
|
283
|
+
await this.#releaseMigrationSource();
|
|
142
284
|
const named = [...this.#connections.entries()];
|
|
143
285
|
const results = await Promise.allSettled(named.map(([, c]) => c.close()));
|
|
144
286
|
this.#connections.clear();
|
|
287
|
+
this.#defaultConnection = undefined;
|
|
145
288
|
this.#booted = false; // allow a fresh boot() after a clean shutdown
|
|
146
289
|
const { clearConnection } = await import("./services/connection.js");
|
|
147
290
|
for (const [, conn] of named)
|
package/dist/EonProvider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EonProvider.js","sourceRoot":"","sources":["../src/EonProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,oBAAoB,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAEN,sBAAsB,GAEtB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"EonProvider.js","sourceRoot":"","sources":["../src/EonProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../src/query/native.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../src/query/native.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkBH,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IACjC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,OAAO,EAAE,CAAC;CAClB;AA0HD;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,UAAuB,GAC9B,iBAAiB,CAKnB;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
package/dist/query/native.js
CHANGED
|
@@ -7,19 +7,7 @@
|
|
|
7
7
|
* throws a descriptive error naming the attempted path — never a silent `null`.
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
|
-
import {
|
|
11
|
-
import { dirname, join } from "node:path";
|
|
12
|
-
import { arch, platform } from "node:process";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
const require2 = createRequire(import.meta.url);
|
|
15
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const platformMap = {
|
|
17
|
-
"linux-x64": "linux-x64-gnu",
|
|
18
|
-
"linux-arm64": "linux-arm64-gnu",
|
|
19
|
-
"darwin-x64": "darwin-x64",
|
|
20
|
-
"darwin-arm64": "darwin-arm64",
|
|
21
|
-
"win32-x64": "win32-x64-msvc",
|
|
22
|
-
};
|
|
10
|
+
import { loadNativeBinary, muslHint, supportedTargets, } from "../vendor/nativeBinary.js";
|
|
23
11
|
let native;
|
|
24
12
|
/**
|
|
25
13
|
* Resolve and cache the NAPI binding. Throws a descriptive error on an
|
|
@@ -30,21 +18,17 @@ let native;
|
|
|
30
18
|
function loadNative() {
|
|
31
19
|
if (native)
|
|
32
20
|
return native;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
binding = require2(binaryPath);
|
|
42
|
-
}
|
|
43
|
-
catch (cause) {
|
|
44
|
-
throw new Error(`[E_EON_NAPI_NOT_FOUND] eon-query native binary not found at '${binaryPath}'. Build it with: pnpm --filter @c9up/eon build:napi`, cause !== undefined ? { cause } : undefined);
|
|
21
|
+
// The resolution is shared; the refusal is not. Both codes below are eon's,
|
|
22
|
+
// and a caller branches on them.
|
|
23
|
+
const attempt = loadNativeBinary();
|
|
24
|
+
if (!attempt.loaded) {
|
|
25
|
+
if (attempt.suffix === undefined) {
|
|
26
|
+
throw new Error(`[E_EON_NAPI_UNSUPPORTED] no eon-query native binary for this platform. Supported: ${supportedTargets().join(", ")}.`);
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`[E_EON_NAPI_NOT_FOUND] eon-query native binary 'index.${attempt.suffix}.node' not found or unloadable.${muslHint()} Build it with: pnpm --filter @c9up/eon build:napi`, attempt.cause !== undefined ? { cause: attempt.cause } : undefined);
|
|
45
29
|
}
|
|
46
|
-
native =
|
|
47
|
-
return
|
|
30
|
+
native = attempt.binary;
|
|
31
|
+
return attempt.binary;
|
|
48
32
|
}
|
|
49
33
|
/**
|
|
50
34
|
* i64 / nanosecond-timestamp precision guard for the JSON compile boundary.
|
package/dist/query/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/query/native.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/query/native.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every `platform-arch` a binary is built for.
|
|
3
|
+
*
|
|
4
|
+
* For the message a package raises when it refuses: "no binary for
|
|
5
|
+
* linux-riscv64" is only actionable next to the list of what there IS.
|
|
6
|
+
*/
|
|
7
|
+
export declare function supportedTargets(): readonly string[];
|
|
8
|
+
/** The suffix a given `platform-arch` builds under, or `undefined`. */
|
|
9
|
+
export declare function nativeTargetSuffix(target: string): string | undefined;
|
|
10
|
+
/** The suffix for THIS platform, or `undefined` where no binary is built. */
|
|
11
|
+
export declare function nativeBinarySuffix(): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Why the binary is not there, phrased for whoever has to fix it.
|
|
14
|
+
*
|
|
15
|
+
* "not found" and "not built for your platform" call for different actions,
|
|
16
|
+
* and a single "unavailable" leaves the reader to guess which one they are
|
|
17
|
+
* looking at.
|
|
18
|
+
*/
|
|
19
|
+
export declare function unavailableReason(cause?: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* `musl` builds under a different suffix, and the failure is otherwise
|
|
22
|
+
* baffling: the file is there, and `require` still refuses it. Worth saying so
|
|
23
|
+
* in a message rather than leaving a reader to discover glibc.
|
|
24
|
+
*/
|
|
25
|
+
export declare function muslHint(): string;
|
|
26
|
+
/** What a load attempt did, so the caller can apply its own policy. */
|
|
27
|
+
export type NativeBinaryResult<T> = {
|
|
28
|
+
loaded: true;
|
|
29
|
+
binary: T;
|
|
30
|
+
} | {
|
|
31
|
+
loaded: false;
|
|
32
|
+
suffix: string | undefined;
|
|
33
|
+
cause?: unknown;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Load `<package root>/index.<suffix>.node`.
|
|
37
|
+
*
|
|
38
|
+
* The path is fixed rather than passed: this file is generated into
|
|
39
|
+
* `<package>/src/vendor/`, so the package root is always two levels up, from
|
|
40
|
+
* `src` and from `dist` alike.
|
|
41
|
+
*/
|
|
42
|
+
export declare function loadNativeBinary<T>(): NativeBinaryResult<T>;
|
|
43
|
+
//# sourceMappingURL=nativeBinary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeBinary.d.ts","sourceRoot":"","sources":["../../src/vendor/nativeBinary.ts"],"names":[],"mappings":"AAoCA;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAEpD;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAQzD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAIjC;AAED,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAC3B;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAElE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CAAC,CAa3D"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Generated from scripts/vendor/nativeBinary.ts — do not edit.
|
|
2
|
+
//
|
|
3
|
+
// This package is published and built from its own repository, so the file
|
|
4
|
+
// has to exist here rather than be imported. `pnpm vendor:sync` rewrites it,
|
|
5
|
+
// and `pnpm vendor:check` fails if this copy has drifted from the original.
|
|
6
|
+
/**
|
|
7
|
+
* Finding the NAPI binary a package ships beside itself.
|
|
8
|
+
*
|
|
9
|
+
* Every package with a Rust engine repeats the same table: napi-rs names its
|
|
10
|
+
* artefacts `index.<platform>-<arch>-<abi>.node`, and the mapping from Node's
|
|
11
|
+
* `platform`/`arch` to that suffix is the part that must never differ between
|
|
12
|
+
* packages. Adding a target means adding it everywhere, and a package that
|
|
13
|
+
* missed the edit fails only on that platform — which is exactly the failure
|
|
14
|
+
* nobody reproduces.
|
|
15
|
+
*
|
|
16
|
+
* What is NOT here is the policy. Some packages degrade when the binary is
|
|
17
|
+
* absent and answer `undefined`; others refuse loudly with build instructions.
|
|
18
|
+
* Both are right for their package, so the loader reports what happened and
|
|
19
|
+
* the caller decides.
|
|
20
|
+
*/
|
|
21
|
+
import { createRequire } from "node:module";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { arch, platform } from "node:process";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
/** Node's `platform-arch` → the suffix napi-rs builds under. */
|
|
26
|
+
const TARGETS = {
|
|
27
|
+
"linux-x64": "linux-x64-gnu",
|
|
28
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
29
|
+
"darwin-x64": "darwin-x64",
|
|
30
|
+
"darwin-arm64": "darwin-arm64",
|
|
31
|
+
"win32-x64": "win32-x64-msvc",
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Every `platform-arch` a binary is built for.
|
|
35
|
+
*
|
|
36
|
+
* For the message a package raises when it refuses: "no binary for
|
|
37
|
+
* linux-riscv64" is only actionable next to the list of what there IS.
|
|
38
|
+
*/
|
|
39
|
+
export function supportedTargets() {
|
|
40
|
+
return Object.keys(TARGETS);
|
|
41
|
+
}
|
|
42
|
+
/** The suffix a given `platform-arch` builds under, or `undefined`. */
|
|
43
|
+
export function nativeTargetSuffix(target) {
|
|
44
|
+
return TARGETS[target];
|
|
45
|
+
}
|
|
46
|
+
/** The suffix for THIS platform, or `undefined` where no binary is built. */
|
|
47
|
+
export function nativeBinarySuffix() {
|
|
48
|
+
return nativeTargetSuffix(`${platform}-${arch}`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Why the binary is not there, phrased for whoever has to fix it.
|
|
52
|
+
*
|
|
53
|
+
* "not found" and "not built for your platform" call for different actions,
|
|
54
|
+
* and a single "unavailable" leaves the reader to guess which one they are
|
|
55
|
+
* looking at.
|
|
56
|
+
*/
|
|
57
|
+
export function unavailableReason(cause) {
|
|
58
|
+
if (cause !== undefined) {
|
|
59
|
+
return `failed to load (${cause instanceof Error ? cause.message : String(cause)})`;
|
|
60
|
+
}
|
|
61
|
+
const target = `${platform}-${arch}`;
|
|
62
|
+
return nativeTargetSuffix(target) !== undefined
|
|
63
|
+
? "binary not found"
|
|
64
|
+
: `no prebuilt binary for ${target}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* `musl` builds under a different suffix, and the failure is otherwise
|
|
68
|
+
* baffling: the file is there, and `require` still refuses it. Worth saying so
|
|
69
|
+
* in a message rather than leaving a reader to discover glibc.
|
|
70
|
+
*/
|
|
71
|
+
export function muslHint() {
|
|
72
|
+
return platform === "linux"
|
|
73
|
+
? " On Alpine/musl, the gnu binary will not load — build the musl target."
|
|
74
|
+
: "";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Load `<package root>/index.<suffix>.node`.
|
|
78
|
+
*
|
|
79
|
+
* The path is fixed rather than passed: this file is generated into
|
|
80
|
+
* `<package>/src/vendor/`, so the package root is always two levels up, from
|
|
81
|
+
* `src` and from `dist` alike.
|
|
82
|
+
*/
|
|
83
|
+
export function loadNativeBinary() {
|
|
84
|
+
const suffix = nativeBinarySuffix();
|
|
85
|
+
if (suffix === undefined)
|
|
86
|
+
return { loaded: false, suffix };
|
|
87
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
88
|
+
try {
|
|
89
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
90
|
+
return {
|
|
91
|
+
loaded: true,
|
|
92
|
+
binary: nodeRequire(join(here, `../../index.${suffix}.node`)),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
return { loaded: false, suffix, cause };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=nativeBinary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeBinary.js","sourceRoot":"","sources":["../../src/vendor/nativeBinary.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAE5E;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,gEAAgE;AAChE,MAAM,OAAO,GAA2B;IACvC,WAAW,EAAE,eAAe;IAC5B,aAAa,EAAE,iBAAiB;IAChC,YAAY,EAAE,YAAY;IAC1B,cAAc,EAAE,cAAc;IAC9B,WAAW,EAAE,gBAAgB;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAChD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB;IACjC,OAAO,kBAAkB,CAAC,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAe;IAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,mBAAmB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;IACrF,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC;IACrC,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,SAAS;QAC9C,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,0BAA0B,MAAM,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACvB,OAAO,QAAQ,KAAK,OAAO;QAC1B,CAAC,CAAC,wEAAwE;QAC1E,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB;IAC/B,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnD,OAAO;YACN,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,MAAM,OAAO,CAAC,CAAM;SAClE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACzC,CAAC;AACF,CAAC"}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/eon",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Eon — time-series data layer for the Ream framework (TDengine-backed)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@biomejs/biome": "^2.4.10",
|
|
61
61
|
"@types/node": "^22",
|
|
62
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
62
63
|
"tsx": "^4.19.0",
|
|
63
64
|
"typescript": "^6.0.2",
|
|
64
65
|
"vitest": "^4.1.2",
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"build:napi-types": "node scripts/build-napi-types.mjs",
|
|
94
95
|
"test": "vitest run",
|
|
95
96
|
"test:rust": "cargo test -p eon-query",
|
|
96
|
-
"lint": "biome check src/",
|
|
97
|
+
"lint": "biome check src/ tests/",
|
|
97
98
|
"test:coverage": "vitest run --coverage",
|
|
98
99
|
"typecheck": "tsc --noEmit"
|
|
99
100
|
}
|
package/src/EonProvider.ts
CHANGED
|
@@ -45,6 +45,16 @@ export interface EonAppContext {
|
|
|
45
45
|
): unknown | Promise<unknown>;
|
|
46
46
|
};
|
|
47
47
|
config: { get<T = unknown>(key: string): T | undefined };
|
|
48
|
+
/**
|
|
49
|
+
* Optional reader — present on ream's real application.
|
|
50
|
+
*
|
|
51
|
+
* `'warmup'` means the application was assembled to be INSPECTED rather than
|
|
52
|
+
* run: a route listing, a codegen pass, a config dump. Providers register,
|
|
53
|
+
* boot and start on that path too, so anything that opens a socket or
|
|
54
|
+
* creates a database has to ask first. A host that does not implement it is
|
|
55
|
+
* treated as running.
|
|
56
|
+
*/
|
|
57
|
+
getMode?(): string;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
@@ -70,25 +80,109 @@ export class EonProvider {
|
|
|
70
80
|
readonly #connections = new Map<string, EonConnection>();
|
|
71
81
|
/** Set once a full boot opened connections — guards against a leaking re-boot. */
|
|
72
82
|
#booted = false;
|
|
83
|
+
/** What the `eon` / `eon.connection` tokens resolve to, once boot opened it. */
|
|
84
|
+
#defaultConnection?: EonConnection;
|
|
73
85
|
|
|
74
86
|
constructor(app: EonAppContext, connect: EonConnector = connectWsEon) {
|
|
75
87
|
this.#app = app;
|
|
76
88
|
this.#connect = connect;
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
register(): void {
|
|
91
|
+
register(): void {
|
|
92
|
+
// Bindings belong here, not in boot(). Upstream registers in `register`
|
|
93
|
+
// precisely so another provider can count on them during its own boot;
|
|
94
|
+
// bound in boot, the container had a different surface depending on
|
|
95
|
+
// which provider ran first, and `eon.compiler` was simply unavailable to
|
|
96
|
+
// anything booting earlier.
|
|
97
|
+
//
|
|
98
|
+
// It is also pure — a compile is a call into the native compiler, no
|
|
99
|
+
// socket, no database — so there is nothing here an inspection must
|
|
100
|
+
// avoid.
|
|
101
|
+
const service: EonService = {
|
|
102
|
+
compile: (spec, dialect) => compileStatementNative(spec, dialect),
|
|
103
|
+
};
|
|
104
|
+
this.#app.container.singleton("eon.compiler", () => service);
|
|
105
|
+
|
|
106
|
+
// The connection tokens are registered here too, so the surface does not
|
|
107
|
+
// change between warmup and run. What they RESOLVE to still depends on
|
|
108
|
+
// boot having opened something — a lazy factory cannot await — so
|
|
109
|
+
// reading one too early says what to do instead of answering undefined.
|
|
110
|
+
this.#app.container.singleton("eon", () => this.#requireDefault("eon"));
|
|
111
|
+
this.#app.container.singleton("eon.connection", () =>
|
|
112
|
+
this.#requireDefault("eon.connection"),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// The NAMED tokens too. Their names come from the config, which is
|
|
116
|
+
// readable here — nothing about `eon:primary` needs a socket to exist.
|
|
117
|
+
// Bound in boot they appeared only on the run path, so the container had
|
|
118
|
+
// a different surface in an inspection, and a boot that failed late left
|
|
119
|
+
// factories behind pointing at connections it had just closed.
|
|
120
|
+
const config =
|
|
121
|
+
this.#app.config.get<EonConfig>("timeseries") ??
|
|
122
|
+
this.#app.config.get<EonConfig>("eon");
|
|
123
|
+
if (!config) return;
|
|
124
|
+
for (const name of Object.keys(
|
|
125
|
+
this.#resolveConnections(config).connections,
|
|
126
|
+
)) {
|
|
127
|
+
this.#app.container.singleton(`eon:${name}`, () =>
|
|
128
|
+
this.#requireNamed(name),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A named connection, or a message saying what is missing.
|
|
135
|
+
*
|
|
136
|
+
* Same reasoning as {@link #requireDefault}: resolving to `undefined` sent
|
|
137
|
+
* the caller into a TypeError several frames from the cause.
|
|
138
|
+
*/
|
|
139
|
+
#requireNamed(name: string): EonConnection {
|
|
140
|
+
const connection = this.#connections.get(name);
|
|
141
|
+
if (connection === undefined) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`EonProvider: connection '${name}' was resolved before it was opened. Connections open in boot(); check that config.timeseries.connections defines '${name}' and that EonProvider is listed before whatever resolved this.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return connection;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The default connection, or a message naming what is missing.
|
|
151
|
+
*
|
|
152
|
+
* Resolving to `undefined` sent the caller into a `TypeError` several
|
|
153
|
+
* frames away from the cause — a provider that resolved eon during its own
|
|
154
|
+
* boot, or an application with no `config/timeseries.ts` at all.
|
|
155
|
+
*/
|
|
156
|
+
#requireDefault(token: string): EonConnection {
|
|
157
|
+
const connection = this.#defaultConnection;
|
|
158
|
+
if (connection === undefined) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`EonProvider: '${token}' was resolved before any connection was opened. Connections open in boot(); check that config.timeseries defines one and that EonProvider is listed before whatever resolved this.`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return connection;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** True when the application was assembled to be inspected rather than run. */
|
|
167
|
+
#isInspecting(): boolean {
|
|
168
|
+
return this.#app.getMode?.() === "warmup";
|
|
169
|
+
}
|
|
80
170
|
|
|
81
171
|
async boot(): Promise<void> {
|
|
172
|
+
// An inspection opens no WebSocket and creates no database. `warmUp()`
|
|
173
|
+
// runs boot, so a route listing reached TDengine — and could CREATE a
|
|
174
|
+
// database there — while `shutdown()` never fires on that path, leaving
|
|
175
|
+
// the sockets open. The compiler binding below is pure and stays.
|
|
82
176
|
if (this.#booted) {
|
|
83
177
|
throw new Error(
|
|
84
178
|
"EonProvider: boot() has already opened connections; a second boot would overwrite and leak them. Call shutdown() first, or construct a new provider.",
|
|
85
179
|
);
|
|
86
180
|
}
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.#
|
|
181
|
+
// Everything here opens a socket or creates a database, so an
|
|
182
|
+
// inspection stops at the compiler. `warmUp()` runs boot, so a route
|
|
183
|
+
// listing reached TDengine — and could CREATE a database there — while
|
|
184
|
+
// `shutdown()` never fires on that path, leaving the sockets open.
|
|
185
|
+
if (this.#isInspecting()) return;
|
|
92
186
|
|
|
93
187
|
const config =
|
|
94
188
|
this.#app.config.get<EonConfig>("timeseries") ??
|
|
@@ -147,24 +241,69 @@ export class EonProvider {
|
|
|
147
241
|
`EonProvider: default connection '${defaultName}' is not defined in config.timeseries.connections`,
|
|
148
242
|
);
|
|
149
243
|
}
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
244
|
+
// Everything past this point runs with the sockets ALREADY OPEN, and any
|
|
245
|
+
// of it can fail: importing the services module, registering the
|
|
246
|
+
// migration source. Without this the failure left the connections open,
|
|
247
|
+
// possibly the module singleton published, and `#booted` false — so the
|
|
248
|
+
// next attempt opened another set on top of the ones nobody could reach.
|
|
249
|
+
try {
|
|
250
|
+
// The tokens were all bound in `register()`; this is what they
|
|
251
|
+
// resolve to. Filling the map is what makes them answer.
|
|
252
|
+
this.#defaultConnection = defaultConn;
|
|
253
|
+
|
|
254
|
+
// Populate the `@c9up/eon/services/connection` singleton so apps can
|
|
255
|
+
// `import connection from '@c9up/eon/services/connection'` anywhere.
|
|
256
|
+
// Lazy import so a type-only discovery scan does not pull the module
|
|
257
|
+
// at construction time.
|
|
258
|
+
const { setConnection } = await import("./services/connection.js");
|
|
259
|
+
setConnection(defaultConn);
|
|
260
|
+
|
|
261
|
+
await this.#registerMigrationSource(defaultConn, config);
|
|
262
|
+
this.#booted = true;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
await this.#rollbackBoot(defaultConn);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Undo a boot that opened its connections and then failed.
|
|
271
|
+
*
|
|
272
|
+
* Everything this releases was created by THIS attempt, so a retry starts
|
|
273
|
+
* from nothing rather than from a half-assembled state. The module
|
|
274
|
+
* singleton is released only while it is still the one we published — two
|
|
275
|
+
* applications can share a process, and the survivor's connection must
|
|
276
|
+
* stay.
|
|
277
|
+
*/
|
|
278
|
+
async #rollbackBoot(published: EonConnection): Promise<void> {
|
|
279
|
+
await this.#releaseMigrationSource();
|
|
280
|
+
// The connector keeps PROCESS-GLOBAL handles, so closing the individual
|
|
281
|
+
// connections is not enough to let Node exit — shutdown() says so and
|
|
282
|
+
// destroys them; a failed boot left them behind and the process hung.
|
|
283
|
+
const hadWebsocket = [...this.#connections.values()].some(
|
|
284
|
+
(conn) => conn.transport === "websocket",
|
|
285
|
+
);
|
|
286
|
+
try {
|
|
287
|
+
const { clearConnection } = await import("./services/connection.js");
|
|
288
|
+
clearConnection(published);
|
|
289
|
+
} catch {
|
|
290
|
+
// The module could not be loaded, so nothing was published from it.
|
|
291
|
+
}
|
|
292
|
+
this.#defaultConnection = undefined;
|
|
293
|
+
await Promise.allSettled(
|
|
294
|
+
[...this.#connections.values()].map((conn) => conn.close()),
|
|
295
|
+
);
|
|
296
|
+
this.#connections.clear();
|
|
297
|
+
if (hadWebsocket) {
|
|
298
|
+
try {
|
|
299
|
+
const { destroyEonConnector } = await import(
|
|
300
|
+
"./connection/websocket.js"
|
|
301
|
+
);
|
|
302
|
+
await destroyEonConnector();
|
|
303
|
+
} catch {
|
|
304
|
+
// A fake-only boot never pulled the connector in.
|
|
305
|
+
}
|
|
155
306
|
}
|
|
156
|
-
this.#app.container.singleton("eon", () => defaultConn);
|
|
157
|
-
this.#app.container.singleton("eon.connection", () => defaultConn);
|
|
158
|
-
|
|
159
|
-
// Populate the `@c9up/eon/services/connection` singleton so apps can
|
|
160
|
-
// `import connection from '@c9up/eon/services/connection'` anywhere. Lazy
|
|
161
|
-
// import so a type-only discovery scan does not pull the module at
|
|
162
|
-
// construction time.
|
|
163
|
-
const { setConnection } = await import("./services/connection.js");
|
|
164
|
-
setConnection(defaultConn);
|
|
165
|
-
|
|
166
|
-
await this.#registerMigrationSource(defaultConn, config);
|
|
167
|
-
this.#booted = true;
|
|
168
307
|
}
|
|
169
308
|
|
|
170
309
|
/**
|
|
@@ -202,11 +341,46 @@ export class EonProvider {
|
|
|
202
341
|
"./schema/EonMigrationRunner.js"
|
|
203
342
|
);
|
|
204
343
|
const migrationsDir = config.migrationsDir ?? "database/eon-migrations";
|
|
205
|
-
|
|
344
|
+
const source = {
|
|
206
345
|
name: "eon",
|
|
207
346
|
directory: migrationsDir,
|
|
208
347
|
runner: new EonMigrationRunner(conn, { migrationsDir }),
|
|
209
|
-
}
|
|
348
|
+
};
|
|
349
|
+
(registry.register as (source: unknown) => unknown)(source);
|
|
350
|
+
// Kept so shutdown can hand the name back. Registering refuses a
|
|
351
|
+
// duplicate, so a provider that stops without releasing its name leaves
|
|
352
|
+
// a second boot in the same process failing on "already registered" —
|
|
353
|
+
// and the CLI driving a runner that holds a closed connection.
|
|
354
|
+
this.#migrationSource = source;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** What this provider registered with the host's migration registry. */
|
|
358
|
+
#migrationSource?: object;
|
|
359
|
+
|
|
360
|
+
/** Give the migration name back, while it is still ours. */
|
|
361
|
+
async #releaseMigrationSource(): Promise<void> {
|
|
362
|
+
const source = this.#migrationSource;
|
|
363
|
+
if (source === undefined) return;
|
|
364
|
+
this.#migrationSource = undefined;
|
|
365
|
+
const resolve = this.#app.container.resolve;
|
|
366
|
+
if (typeof resolve !== "function") return;
|
|
367
|
+
try {
|
|
368
|
+
const registry = await resolve.call(this.#app.container, "migrations");
|
|
369
|
+
if (
|
|
370
|
+
typeof registry === "object" &&
|
|
371
|
+
registry !== null &&
|
|
372
|
+
typeof Reflect.get(registry, "unregister") === "function"
|
|
373
|
+
) {
|
|
374
|
+
(
|
|
375
|
+
registry as {
|
|
376
|
+
unregister: (name: string, source?: unknown) => unknown;
|
|
377
|
+
}
|
|
378
|
+
).unregister("eon", source);
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
// No registry, or an older host without `unregister`: nothing to give
|
|
382
|
+
// back, and a shutdown must not fail over it.
|
|
383
|
+
}
|
|
210
384
|
}
|
|
211
385
|
|
|
212
386
|
async shutdown(): Promise<void> {
|
|
@@ -214,9 +388,11 @@ export class EonProvider {
|
|
|
214
388
|
// stuck close doesn't block the rest, but failures are aggregated and
|
|
215
389
|
// rethrown so supervisors see a non-zero shutdown signal. The map + module
|
|
216
390
|
// singleton are cleared unconditionally (never hand out closed handles).
|
|
391
|
+
await this.#releaseMigrationSource();
|
|
217
392
|
const named = [...this.#connections.entries()];
|
|
218
393
|
const results = await Promise.allSettled(named.map(([, c]) => c.close()));
|
|
219
394
|
this.#connections.clear();
|
|
395
|
+
this.#defaultConnection = undefined;
|
|
220
396
|
this.#booted = false; // allow a fresh boot() after a clean shutdown
|
|
221
397
|
|
|
222
398
|
const { clearConnection } = await import("./services/connection.js");
|
package/src/query/native.ts
CHANGED
|
@@ -8,21 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const require2 = createRequire(import.meta.url);
|
|
17
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
const platformMap: Record<string, string> = {
|
|
20
|
-
"linux-x64": "linux-x64-gnu",
|
|
21
|
-
"linux-arm64": "linux-arm64-gnu",
|
|
22
|
-
"darwin-x64": "darwin-x64",
|
|
23
|
-
"darwin-arm64": "darwin-arm64",
|
|
24
|
-
"win32-x64": "win32-x64-msvc",
|
|
25
|
-
};
|
|
11
|
+
import {
|
|
12
|
+
loadNativeBinary,
|
|
13
|
+
muslHint,
|
|
14
|
+
supportedTargets,
|
|
15
|
+
} from "../vendor/nativeBinary.js";
|
|
26
16
|
|
|
27
17
|
/**
|
|
28
18
|
* The compiler's surface, as the Rust declares it.
|
|
@@ -51,25 +41,22 @@ let native: NativeBinding | undefined;
|
|
|
51
41
|
*/
|
|
52
42
|
function loadNative(): NativeBinding {
|
|
53
43
|
if (native) return native;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
binding = require2(binaryPath);
|
|
65
|
-
} catch (cause) {
|
|
44
|
+
// The resolution is shared; the refusal is not. Both codes below are eon's,
|
|
45
|
+
// and a caller branches on them.
|
|
46
|
+
const attempt = loadNativeBinary<NativeBinding>();
|
|
47
|
+
if (!attempt.loaded) {
|
|
48
|
+
if (attempt.suffix === undefined) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`[E_EON_NAPI_UNSUPPORTED] no eon-query native binary for this platform. Supported: ${supportedTargets().join(", ")}.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
66
53
|
throw new Error(
|
|
67
|
-
`[E_EON_NAPI_NOT_FOUND] eon-query native binary not found
|
|
68
|
-
cause !== undefined ? { cause } : undefined,
|
|
54
|
+
`[E_EON_NAPI_NOT_FOUND] eon-query native binary 'index.${attempt.suffix}.node' not found or unloadable.${muslHint()} Build it with: pnpm --filter @c9up/eon build:napi`,
|
|
55
|
+
attempt.cause !== undefined ? { cause: attempt.cause } : undefined,
|
|
69
56
|
);
|
|
70
57
|
}
|
|
71
|
-
native =
|
|
72
|
-
return
|
|
58
|
+
native = attempt.binary;
|
|
59
|
+
return attempt.binary;
|
|
73
60
|
}
|
|
74
61
|
|
|
75
62
|
/**
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Generated from scripts/vendor/nativeBinary.ts — do not edit.
|
|
2
|
+
//
|
|
3
|
+
// This package is published and built from its own repository, so the file
|
|
4
|
+
// has to exist here rather than be imported. `pnpm vendor:sync` rewrites it,
|
|
5
|
+
// and `pnpm vendor:check` fails if this copy has drifted from the original.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Finding the NAPI binary a package ships beside itself.
|
|
9
|
+
*
|
|
10
|
+
* Every package with a Rust engine repeats the same table: napi-rs names its
|
|
11
|
+
* artefacts `index.<platform>-<arch>-<abi>.node`, and the mapping from Node's
|
|
12
|
+
* `platform`/`arch` to that suffix is the part that must never differ between
|
|
13
|
+
* packages. Adding a target means adding it everywhere, and a package that
|
|
14
|
+
* missed the edit fails only on that platform — which is exactly the failure
|
|
15
|
+
* nobody reproduces.
|
|
16
|
+
*
|
|
17
|
+
* What is NOT here is the policy. Some packages degrade when the binary is
|
|
18
|
+
* absent and answer `undefined`; others refuse loudly with build instructions.
|
|
19
|
+
* Both are right for their package, so the loader reports what happened and
|
|
20
|
+
* the caller decides.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { createRequire } from "node:module";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
import { arch, platform } from "node:process";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
|
|
28
|
+
/** Node's `platform-arch` → the suffix napi-rs builds under. */
|
|
29
|
+
const TARGETS: Record<string, string> = {
|
|
30
|
+
"linux-x64": "linux-x64-gnu",
|
|
31
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
32
|
+
"darwin-x64": "darwin-x64",
|
|
33
|
+
"darwin-arm64": "darwin-arm64",
|
|
34
|
+
"win32-x64": "win32-x64-msvc",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Every `platform-arch` a binary is built for.
|
|
39
|
+
*
|
|
40
|
+
* For the message a package raises when it refuses: "no binary for
|
|
41
|
+
* linux-riscv64" is only actionable next to the list of what there IS.
|
|
42
|
+
*/
|
|
43
|
+
export function supportedTargets(): readonly string[] {
|
|
44
|
+
return Object.keys(TARGETS);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The suffix a given `platform-arch` builds under, or `undefined`. */
|
|
48
|
+
export function nativeTargetSuffix(target: string): string | undefined {
|
|
49
|
+
return TARGETS[target];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The suffix for THIS platform, or `undefined` where no binary is built. */
|
|
53
|
+
export function nativeBinarySuffix(): string | undefined {
|
|
54
|
+
return nativeTargetSuffix(`${platform}-${arch}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Why the binary is not there, phrased for whoever has to fix it.
|
|
59
|
+
*
|
|
60
|
+
* "not found" and "not built for your platform" call for different actions,
|
|
61
|
+
* and a single "unavailable" leaves the reader to guess which one they are
|
|
62
|
+
* looking at.
|
|
63
|
+
*/
|
|
64
|
+
export function unavailableReason(cause?: unknown): string {
|
|
65
|
+
if (cause !== undefined) {
|
|
66
|
+
return `failed to load (${cause instanceof Error ? cause.message : String(cause)})`;
|
|
67
|
+
}
|
|
68
|
+
const target = `${platform}-${arch}`;
|
|
69
|
+
return nativeTargetSuffix(target) !== undefined
|
|
70
|
+
? "binary not found"
|
|
71
|
+
: `no prebuilt binary for ${target}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `musl` builds under a different suffix, and the failure is otherwise
|
|
76
|
+
* baffling: the file is there, and `require` still refuses it. Worth saying so
|
|
77
|
+
* in a message rather than leaving a reader to discover glibc.
|
|
78
|
+
*/
|
|
79
|
+
export function muslHint(): string {
|
|
80
|
+
return platform === "linux"
|
|
81
|
+
? " On Alpine/musl, the gnu binary will not load — build the musl target."
|
|
82
|
+
: "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** What a load attempt did, so the caller can apply its own policy. */
|
|
86
|
+
export type NativeBinaryResult<T> =
|
|
87
|
+
| { loaded: true; binary: T }
|
|
88
|
+
| { loaded: false; suffix: string | undefined; cause?: unknown };
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Load `<package root>/index.<suffix>.node`.
|
|
92
|
+
*
|
|
93
|
+
* The path is fixed rather than passed: this file is generated into
|
|
94
|
+
* `<package>/src/vendor/`, so the package root is always two levels up, from
|
|
95
|
+
* `src` and from `dist` alike.
|
|
96
|
+
*/
|
|
97
|
+
export function loadNativeBinary<T>(): NativeBinaryResult<T> {
|
|
98
|
+
const suffix = nativeBinarySuffix();
|
|
99
|
+
if (suffix === undefined) return { loaded: false, suffix };
|
|
100
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
101
|
+
try {
|
|
102
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
103
|
+
return {
|
|
104
|
+
loaded: true,
|
|
105
|
+
binary: nodeRequire(join(here, `../../index.${suffix}.node`)) as T,
|
|
106
|
+
};
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
return { loaded: false, suffix, cause };
|
|
109
|
+
}
|
|
110
|
+
}
|