@c9up/rover 0.1.16 → 0.1.17
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/RoverProvider.d.ts +7 -0
- package/dist/RoverProvider.d.ts.map +1 -1
- package/dist/RoverProvider.js +23 -7
- package/dist/RoverProvider.js.map +1 -1
- package/dist/services/main.d.ts +9 -0
- package/dist/services/main.d.ts.map +1 -1
- package/dist/services/main.js +11 -0
- package/dist/services/main.js.map +1 -1
- package/dist/templating/loadNapi.d.ts.map +1 -1
- package/dist/templating/loadNapi.js +7 -16
- package/dist/templating/loadNapi.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 +2 -2
- package/src/RoverProvider.ts +29 -6
- package/src/services/main.ts +12 -0
- package/src/templating/loadNapi.ts +11 -17
- package/src/vendor/nativeBinary.ts +110 -0
package/dist/RoverProvider.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ import "./augmentations.js";
|
|
|
6
6
|
interface RoverContainer {
|
|
7
7
|
singleton(token: unknown, factory: () => unknown): void;
|
|
8
8
|
resolve<T = unknown>(token: unknown): Promise<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Optional reader — present on ream's real container. Asked before resolving
|
|
11
|
+
* an OPTIONAL peer, so "not installed" is answered by a lookup rather than
|
|
12
|
+
* inferred from whatever resolving it threw.
|
|
13
|
+
*/
|
|
14
|
+
has?(token: unknown): boolean;
|
|
9
15
|
}
|
|
10
16
|
interface RoverConfigStore {
|
|
11
17
|
get<T = unknown>(key: string): T | undefined;
|
|
@@ -15,6 +21,7 @@ export interface RoverAppContext {
|
|
|
15
21
|
config: RoverConfigStore;
|
|
16
22
|
}
|
|
17
23
|
export default class RoverProvider {
|
|
24
|
+
#private;
|
|
18
25
|
protected app: RoverAppContext;
|
|
19
26
|
constructor(app: RoverAppContext);
|
|
20
27
|
register(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoverProvider.d.ts","sourceRoot":"","sources":["../src/RoverProvider.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAM5B;;;GAGG;AACH,UAAU,cAAc;IACvB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"RoverProvider.d.ts","sourceRoot":"","sources":["../src/RoverProvider.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAM5B;;;GAGG;AACH,UAAU,cAAc;IACvB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD;;;;OAIG;IACH,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CAC9B;AACD,UAAU,gBAAgB;IACzB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,cAAc,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,CAAC,OAAO,OAAO,aAAa;;IAIrB,SAAS,CAAC,GAAG,EAAE,eAAe;gBAApB,GAAG,EAAE,eAAe;IAE1C,QAAQ;IA+BF,IAAI;IASJ,QAAQ;CAQd"}
|
package/dist/RoverProvider.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import "./augmentations.js";
|
|
2
2
|
import { Mail } from "./Mail.js";
|
|
3
|
-
import { setMail } from "./services/main.js";
|
|
3
|
+
import { clearMail, getMail, setMail } from "./services/main.js";
|
|
4
4
|
export default class RoverProvider {
|
|
5
5
|
app;
|
|
6
|
+
/** The Mail this provider bound, so shutdown only clears its own. */
|
|
7
|
+
#mail;
|
|
6
8
|
constructor(app) {
|
|
7
9
|
this.app = app;
|
|
8
10
|
}
|
|
@@ -37,9 +39,19 @@ export default class RoverProvider {
|
|
|
37
39
|
// Populate the `@c9up/rover/services/main` singleton with the
|
|
38
40
|
// container-resolved Mail instance so apps can
|
|
39
41
|
// `import mail from '@c9up/rover/services/main'` from anywhere.
|
|
40
|
-
|
|
42
|
+
const mail = await this.app.container.resolve(Mail);
|
|
43
|
+
this.#mail = mail;
|
|
44
|
+
setMail(mail);
|
|
45
|
+
}
|
|
46
|
+
async shutdown() {
|
|
47
|
+
// Release the module-level singleton, but only while it is still ours: a
|
|
48
|
+
// stopped application left a dead Mail reachable through
|
|
49
|
+
// `services/main`, and with two applications in one process the one
|
|
50
|
+
// shutting down must not clear a binding the other has since installed.
|
|
51
|
+
if (this.#mail !== undefined && getMail() === this.#mail)
|
|
52
|
+
clearMail();
|
|
53
|
+
this.#mail = undefined;
|
|
41
54
|
}
|
|
42
|
-
async shutdown() { }
|
|
43
55
|
}
|
|
44
56
|
/**
|
|
45
57
|
* Resolve a token from the container without throwing when it's not
|
|
@@ -47,11 +59,15 @@ export default class RoverProvider {
|
|
|
47
59
|
* are purely opt-in.
|
|
48
60
|
*/
|
|
49
61
|
async function tryResolve(app, token) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
// ASK whether the token is bound, rather than catching whatever resolving it
|
|
63
|
+
// throws. Catching everything meant a registered binding whose factory
|
|
64
|
+
// failed — a bad queue config, a driver that cannot connect — read exactly
|
|
65
|
+
// like "bay is not installed": rover disabled the feature and said nothing,
|
|
66
|
+
// and mail queued nowhere. Absence is a `has()` answer; a factory that
|
|
67
|
+
// throws is a fault, and faults propagate.
|
|
68
|
+
if (typeof app.container.has === "function" && !app.container.has(token)) {
|
|
54
69
|
return undefined;
|
|
55
70
|
}
|
|
71
|
+
return await app.container.resolve(token);
|
|
56
72
|
}
|
|
57
73
|
//# sourceMappingURL=RoverProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoverProvider.js","sourceRoot":"","sources":["../src/RoverProvider.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"RoverProvider.js","sourceRoot":"","sources":["../src/RoverProvider.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAwBjE,MAAM,CAAC,OAAO,OAAO,aAAa;IAIX;IAHtB,qEAAqE;IACrE,KAAK,CAAmB;IAExB,YAAsB,GAAoB;QAApB,QAAG,GAAH,GAAG,CAAiB;IAAG,CAAC;IAE9C,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAa,MAAM,CAAC,CAAC;YACvD,OAAO,IAAI,IAAI,CACd,MAAM,IAAI;gBACT,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;aACtC,EACD;gBACC,4DAA4D;gBAC5D,2DAA2D;gBAC3D,yDAAyD;gBACzD,yDAAyD;gBACzD,KAAK,EAAE,MAAM,UAAU,CAAe,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC;gBAC/D,iEAAiE;gBACjE,2CAA2C;gBAC3C,OAAO,EAAE,MAAM,UAAU,CAAc,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC;aAC3D,CACD,CAAC;QACH,CAAC,CAAC,CAAC;QACH,sEAAsE;QACtE,qEAAqE;QACrE,yDAAyD;QACzD,uEAAuE;QACvE,oBAAoB;QACpB,MAAM,IAAI,GAAG,GAAkB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAO,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI;QACT,8DAA8D;QAC9D,+CAA+C;QAC/C,gEAAgE;QAChE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAO,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,KAAK,CAAC,QAAQ;QACb,yEAAyE;QACzE,yDAAyD;QACzD,oEAAoE;QACpE,wEAAwE;QACxE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,EAAE,KAAK,IAAI,CAAC,KAAK;YAAE,SAAS,EAAE,CAAC;QACtE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,CAAC;CACD;AAED;;;;GAIG;AACH,KAAK,UAAU,UAAU,CACxB,GAAoB,EACpB,KAAa;IAEb,6EAA6E;IAC7E,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,2CAA2C;IAC3C,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1E,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAAI,KAAK,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/services/main.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ import type { Mail } from "../Mail.js";
|
|
|
15
15
|
export declare function setMail(mail: Mail): void;
|
|
16
16
|
/** @internal Get the resolved singleton (or `undefined` pre-boot). */
|
|
17
17
|
export declare function getMail(): Mail | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* @internal Release the singleton, so a shut-down application does not leave a
|
|
20
|
+
* dead Mail reachable through `services/main`.
|
|
21
|
+
*
|
|
22
|
+
* The caller checks ownership first (`getMail() === mine`): with two
|
|
23
|
+
* applications in one process, the one shutting down must not clear a binding
|
|
24
|
+
* the other has since installed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearMail(): void;
|
|
18
27
|
declare const mail: Mail;
|
|
19
28
|
export default mail;
|
|
20
29
|
//# sourceMappingURL=main.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAExC;AAED,sEAAsE;AACtE,wBAAgB,OAAO,IAAI,IAAI,GAAG,SAAS,CAE1C;AAED,QAAA,MAAM,IAAI,EAAE,IAWV,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAExC;AAED,sEAAsE;AACtE,wBAAgB,OAAO,IAAI,IAAI,GAAG,SAAS,CAE1C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAED,QAAA,MAAM,IAAI,EAAE,IAWV,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
package/dist/services/main.js
CHANGED
|
@@ -19,6 +19,17 @@ export function setMail(mail) {
|
|
|
19
19
|
export function getMail() {
|
|
20
20
|
return instance;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* @internal Release the singleton, so a shut-down application does not leave a
|
|
24
|
+
* dead Mail reachable through `services/main`.
|
|
25
|
+
*
|
|
26
|
+
* The caller checks ownership first (`getMail() === mine`): with two
|
|
27
|
+
* applications in one process, the one shutting down must not clear a binding
|
|
28
|
+
* the other has since installed.
|
|
29
|
+
*/
|
|
30
|
+
export function clearMail() {
|
|
31
|
+
instance = undefined;
|
|
32
|
+
}
|
|
22
33
|
const mail = new Proxy({}, {
|
|
23
34
|
get(_target, prop) {
|
|
24
35
|
if (!instance) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,IAAI,QAA0B,CAAC;AAE/B,2EAA2E;AAC3E,MAAM,UAAU,OAAO,CAAC,IAAU;IACjC,QAAQ,GAAG,IAAI,CAAC;AACjB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,OAAO;IACtB,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,MAAM,IAAI,GAAS,IAAI,KAAK,CAAC,EAAU,EAAE;IACxC,GAAG,CAAC,OAAO,EAAE,IAAI;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACd,mEAAmE;gBAClE,0EAA0E,CAC3E,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpD,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnE,CAAC;CACD,CAAC,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,IAAI,QAA0B,CAAC;AAE/B,2EAA2E;AAC3E,MAAM,UAAU,OAAO,CAAC,IAAU;IACjC,QAAQ,GAAG,IAAI,CAAC;AACjB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,OAAO;IACtB,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS;IACxB,QAAQ,GAAG,SAAS,CAAC;AACtB,CAAC;AAED,MAAM,IAAI,GAAS,IAAI,KAAK,CAAC,EAAU,EAAE;IACxC,GAAG,CAAC,OAAO,EAAE,IAAI;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACd,mEAAmE;gBAClE,0EAA0E,CAC3E,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpD,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnE,CAAC;CACD,CAAC,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadNapi.d.ts","sourceRoot":"","sources":["../../src/templating/loadNapi.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loadNapi.d.ts","sourceRoot":"","sources":["../../src/templating/loadNapi.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAsB9C,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,OAAO,wBAAwB,EAAE,OAAO,CAAC;AAErE;;;;;;;;GAQG;AACH,KAAK,aAAa,GAAG,cAAc,wBAAwB,CAAC,CAAC;AAa7D,wBAAgB,SAAS,IAAI,aAAa,CA2CzC;AA6BD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAwBpD"}
|
|
@@ -7,21 +7,14 @@
|
|
|
7
7
|
// consumers get a typed `RoverError`. Zero `as` / `any` per `feedback_no_any_types`
|
|
8
8
|
// — every boundary is narrowed with a `Reflect.get` type guard.
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
|
-
import { arch, platform } from "node:process";
|
|
11
10
|
import { fileURLToPath } from "node:url";
|
|
12
11
|
import { RoverError } from "../RoverError.js";
|
|
13
|
-
|
|
14
|
-
"linux-x64": "linux-x64-gnu",
|
|
15
|
-
"linux-arm64": "linux-arm64-gnu",
|
|
16
|
-
"darwin-x64": "darwin-x64",
|
|
17
|
-
"darwin-arm64": "darwin-arm64",
|
|
18
|
-
"win32-x64": "win32-x64-msvc",
|
|
19
|
-
};
|
|
12
|
+
import { muslHint, nativeBinarySuffix, supportedTargets, } from "../vendor/nativeBinary.js";
|
|
20
13
|
function platformSuffix() {
|
|
21
|
-
|
|
22
|
-
const suffix =
|
|
23
|
-
if (
|
|
24
|
-
throw new RoverError("E_MAIL_TEMPLATE_NAPI_REQUIRED", `
|
|
14
|
+
// The table is shared; the refusal is rover's, with rover's code on it.
|
|
15
|
+
const suffix = nativeBinarySuffix();
|
|
16
|
+
if (suffix === undefined) {
|
|
17
|
+
throw new RoverError("E_MAIL_TEMPLATE_NAPI_REQUIRED", `No @c9up/rover native binary for this platform. Supported: ${supportedTargets().join(", ")}.`, {
|
|
25
18
|
hint: "Build the native binary on a supported platform with 'pnpm --filter @c9up/rover build:napi'.",
|
|
26
19
|
});
|
|
27
20
|
}
|
|
@@ -58,10 +51,8 @@ export function getNative() {
|
|
|
58
51
|
// The prebuilt linux binaries target glibc (`-gnu`). On musl hosts (Alpine
|
|
59
52
|
// containers) the `-gnu` binary fails to dlopen with a libc symbol error —
|
|
60
53
|
// surface that explicitly rather than only pointing at the build step.
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
: "";
|
|
64
|
-
throw new RoverError("E_MAIL_TEMPLATE_NAPI_REQUIRED", `@c9up/rover native binary 'index.${suffix}.node' not found or failed to load near ${here} — run 'pnpm --filter @c9up/rover build:napi' to build it.${muslHint} Cause: ${causeMessage}`, {
|
|
54
|
+
const musl = muslHint();
|
|
55
|
+
throw new RoverError("E_MAIL_TEMPLATE_NAPI_REQUIRED", `@c9up/rover native binary 'index.${suffix}.node' not found or failed to load near ${here} — run 'pnpm --filter @c9up/rover build:napi' to build it.${musl} Cause: ${causeMessage}`, {
|
|
65
56
|
hint: "Run 'pnpm --filter @c9up/rover build:napi' to compile the native template engine.",
|
|
66
57
|
});
|
|
67
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadNapi.js","sourceRoot":"","sources":["../../src/templating/loadNapi.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,yDAAyD;AACzD,wEAAwE;AACxE,0CAA0C;AAC1C,EAAE;AACF,gFAAgF;AAChF,oFAAoF;AACpF,gEAAgE;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"loadNapi.js","sourceRoot":"","sources":["../../src/templating/loadNapi.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/rover",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Mail transport for Ream — SMTP, log, and pluggable transports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@biomejs/biome": "^2.4.10",
|
|
71
|
-
"@c9up/bay": "^0.
|
|
71
|
+
"@c9up/bay": "^0.2.0",
|
|
72
72
|
"@c9up/helix": "^0.2.0",
|
|
73
73
|
"@c9up/ream": "^0.2.0",
|
|
74
74
|
"@types/node": "*",
|
package/src/RoverProvider.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "./augmentations.js";
|
|
|
2
2
|
import type { EmitterLike, MailConfig } from "./Mail.js";
|
|
3
3
|
import { Mail } from "./Mail.js";
|
|
4
4
|
import type { BayQueueLike } from "./queue/MailJob.js";
|
|
5
|
-
import { setMail } from "./services/main.js";
|
|
5
|
+
import { clearMail, getMail, setMail } from "./services/main.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Duck-typed slice of the host's IoC container — rover does NOT import
|
|
@@ -11,6 +11,12 @@ import { setMail } from "./services/main.js";
|
|
|
11
11
|
interface RoverContainer {
|
|
12
12
|
singleton(token: unknown, factory: () => unknown): void;
|
|
13
13
|
resolve<T = unknown>(token: unknown): Promise<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Optional reader — present on ream's real container. Asked before resolving
|
|
16
|
+
* an OPTIONAL peer, so "not installed" is answered by a lookup rather than
|
|
17
|
+
* inferred from whatever resolving it threw.
|
|
18
|
+
*/
|
|
19
|
+
has?(token: unknown): boolean;
|
|
14
20
|
}
|
|
15
21
|
interface RoverConfigStore {
|
|
16
22
|
get<T = unknown>(key: string): T | undefined;
|
|
@@ -21,6 +27,9 @@ export interface RoverAppContext {
|
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export default class RoverProvider {
|
|
30
|
+
/** The Mail this provider bound, so shutdown only clears its own. */
|
|
31
|
+
#mail: Mail | undefined;
|
|
32
|
+
|
|
24
33
|
constructor(protected app: RoverAppContext) {}
|
|
25
34
|
|
|
26
35
|
register() {
|
|
@@ -58,10 +67,19 @@ export default class RoverProvider {
|
|
|
58
67
|
// Populate the `@c9up/rover/services/main` singleton with the
|
|
59
68
|
// container-resolved Mail instance so apps can
|
|
60
69
|
// `import mail from '@c9up/rover/services/main'` from anywhere.
|
|
61
|
-
|
|
70
|
+
const mail = await this.app.container.resolve<Mail>(Mail);
|
|
71
|
+
this.#mail = mail;
|
|
72
|
+
setMail(mail);
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
async shutdown() {
|
|
75
|
+
async shutdown() {
|
|
76
|
+
// Release the module-level singleton, but only while it is still ours: a
|
|
77
|
+
// stopped application left a dead Mail reachable through
|
|
78
|
+
// `services/main`, and with two applications in one process the one
|
|
79
|
+
// shutting down must not clear a binding the other has since installed.
|
|
80
|
+
if (this.#mail !== undefined && getMail() === this.#mail) clearMail();
|
|
81
|
+
this.#mail = undefined;
|
|
82
|
+
}
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
/**
|
|
@@ -73,9 +91,14 @@ async function tryResolve<T>(
|
|
|
73
91
|
app: RoverAppContext,
|
|
74
92
|
token: string,
|
|
75
93
|
): Promise<T | undefined> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
// ASK whether the token is bound, rather than catching whatever resolving it
|
|
95
|
+
// throws. Catching everything meant a registered binding whose factory
|
|
96
|
+
// failed — a bad queue config, a driver that cannot connect — read exactly
|
|
97
|
+
// like "bay is not installed": rover disabled the feature and said nothing,
|
|
98
|
+
// and mail queued nowhere. Absence is a `has()` answer; a factory that
|
|
99
|
+
// throws is a fault, and faults propagate.
|
|
100
|
+
if (typeof app.container.has === "function" && !app.container.has(token)) {
|
|
79
101
|
return undefined;
|
|
80
102
|
}
|
|
103
|
+
return await app.container.resolve<T>(token);
|
|
81
104
|
}
|
package/src/services/main.ts
CHANGED
|
@@ -25,6 +25,18 @@ export function getMail(): Mail | undefined {
|
|
|
25
25
|
return instance;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @internal Release the singleton, so a shut-down application does not leave a
|
|
30
|
+
* dead Mail reachable through `services/main`.
|
|
31
|
+
*
|
|
32
|
+
* The caller checks ownership first (`getMail() === mine`): with two
|
|
33
|
+
* applications in one process, the one shutting down must not clear a binding
|
|
34
|
+
* the other has since installed.
|
|
35
|
+
*/
|
|
36
|
+
export function clearMail(): void {
|
|
37
|
+
instance = undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
const mail: Mail = new Proxy({} as Mail, {
|
|
29
41
|
get(_target, prop) {
|
|
30
42
|
if (!instance) {
|
|
@@ -8,25 +8,21 @@
|
|
|
8
8
|
// — every boundary is narrowed with a `Reflect.get` type guard.
|
|
9
9
|
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
|
-
import { arch, platform } from "node:process";
|
|
12
11
|
import { fileURLToPath } from "node:url";
|
|
13
12
|
import { RoverError } from "../RoverError.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"darwin-arm64": "darwin-arm64",
|
|
20
|
-
"win32-x64": "win32-x64-msvc",
|
|
21
|
-
};
|
|
13
|
+
import {
|
|
14
|
+
muslHint,
|
|
15
|
+
nativeBinarySuffix,
|
|
16
|
+
supportedTargets,
|
|
17
|
+
} from "../vendor/nativeBinary.js";
|
|
22
18
|
|
|
23
19
|
function platformSuffix(): string {
|
|
24
|
-
|
|
25
|
-
const suffix =
|
|
26
|
-
if (
|
|
20
|
+
// The table is shared; the refusal is rover's, with rover's code on it.
|
|
21
|
+
const suffix = nativeBinarySuffix();
|
|
22
|
+
if (suffix === undefined) {
|
|
27
23
|
throw new RoverError(
|
|
28
24
|
"E_MAIL_TEMPLATE_NAPI_REQUIRED",
|
|
29
|
-
`
|
|
25
|
+
`No @c9up/rover native binary for this platform. Supported: ${supportedTargets().join(", ")}.`,
|
|
30
26
|
{
|
|
31
27
|
hint: "Build the native binary on a supported platform with 'pnpm --filter @c9up/rover build:napi'.",
|
|
32
28
|
},
|
|
@@ -83,12 +79,10 @@ export function getNative(): NativeExports {
|
|
|
83
79
|
// The prebuilt linux binaries target glibc (`-gnu`). On musl hosts (Alpine
|
|
84
80
|
// containers) the `-gnu` binary fails to dlopen with a libc symbol error —
|
|
85
81
|
// surface that explicitly rather than only pointing at the build step.
|
|
86
|
-
const
|
|
87
|
-
? " If you are on Alpine/musl, note the prebuilt binaries target glibc (musl is not a supported target)."
|
|
88
|
-
: "";
|
|
82
|
+
const musl = muslHint();
|
|
89
83
|
throw new RoverError(
|
|
90
84
|
"E_MAIL_TEMPLATE_NAPI_REQUIRED",
|
|
91
|
-
`@c9up/rover native binary 'index.${suffix}.node' not found or failed to load near ${here} — run 'pnpm --filter @c9up/rover build:napi' to build it.${
|
|
85
|
+
`@c9up/rover native binary 'index.${suffix}.node' not found or failed to load near ${here} — run 'pnpm --filter @c9up/rover build:napi' to build it.${musl} Cause: ${causeMessage}`,
|
|
92
86
|
{
|
|
93
87
|
hint: "Run 'pnpm --filter @c9up/rover build:napi' to compile the native template engine.",
|
|
94
88
|
},
|
|
@@ -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
|
+
}
|