relaxnative 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +592 -0
- package/dist/chunk-22W76CYR.js +607 -0
- package/dist/chunk-24NXCU65.js +254 -0
- package/dist/chunk-2APMRURB.js +65 -0
- package/dist/chunk-2CHBHJPT.js +607 -0
- package/dist/chunk-2I4JHZI7.js +287 -0
- package/dist/chunk-2JOHYYQO.js +607 -0
- package/dist/chunk-3GW77EWF.js +505 -0
- package/dist/chunk-5J5CAKCD.js +266 -0
- package/dist/chunk-5NTDZ7YZ.js +377 -0
- package/dist/chunk-5TA6MROS.js +529 -0
- package/dist/chunk-5WVEBKMJ.js +1019 -0
- package/dist/chunk-6O5TIEEI.js +545 -0
- package/dist/chunk-6XU5DETO.js +896 -0
- package/dist/chunk-7BIZ6P3B.js +176 -0
- package/dist/chunk-7DKO777J.js +285 -0
- package/dist/chunk-7JYWUH4Y.js +268 -0
- package/dist/chunk-7NMCEP2V.js +756 -0
- package/dist/chunk-A7N4YBP2.js +379 -0
- package/dist/chunk-AZTCDV6R.js +572 -0
- package/dist/chunk-B34XEGM6.js +559 -0
- package/dist/chunk-BFHBLVXW.js +607 -0
- package/dist/chunk-BLOJ33LO.js +65 -0
- package/dist/chunk-BYPXCWTI.js +375 -0
- package/dist/chunk-C4KJD2AN.js +1044 -0
- package/dist/chunk-CJALJTRQ.js +814 -0
- package/dist/chunk-D4PK367Z.js +627 -0
- package/dist/chunk-DCWBZPEV.js +287 -0
- package/dist/chunk-DI7KSUEC.js +676 -0
- package/dist/chunk-DQ2KXIOO.js +665 -0
- package/dist/chunk-DV5STE3W.js +986 -0
- package/dist/chunk-EG5KNEKP.js +1027 -0
- package/dist/chunk-EOA2OWFA.js +1020 -0
- package/dist/chunk-ES3B6EZJ.js +56 -0
- package/dist/chunk-ETIXNPU5.js +741 -0
- package/dist/chunk-EUZBU2H7.js +824 -0
- package/dist/chunk-F6V7XDEB.js +150 -0
- package/dist/chunk-FNJKUFNF.js +1019 -0
- package/dist/chunk-FZB37DWL.js +453 -0
- package/dist/chunk-G3NDHZNZ.js +453 -0
- package/dist/chunk-G4YR34LE.js +410 -0
- package/dist/chunk-GU4XXISM.js +264 -0
- package/dist/chunk-GVPSQXGJ.js +1027 -0
- package/dist/chunk-HD5C4RNU.js +676 -0
- package/dist/chunk-HDIVY47T.js +287 -0
- package/dist/chunk-HFLRTDNK.js +985 -0
- package/dist/chunk-HGWRCVQ5.js +287 -0
- package/dist/chunk-HRG3SVKK.js +995 -0
- package/dist/chunk-HUGFULJ3.js +1027 -0
- package/dist/chunk-IDYSBXYS.js +344 -0
- package/dist/chunk-ISDDUQVI.js +1019 -0
- package/dist/chunk-IZ632ZCJ.js +286 -0
- package/dist/chunk-J5XI4L52.js +218 -0
- package/dist/chunk-JTIO7BUH.js +582 -0
- package/dist/chunk-JTWSFMF2.js +1020 -0
- package/dist/chunk-K5TV62T4.js +736 -0
- package/dist/chunk-K7MTG53V.js +985 -0
- package/dist/chunk-KGLZB3H2.js +676 -0
- package/dist/chunk-KYAB35P5.js +741 -0
- package/dist/chunk-KYDW3YVX.js +453 -0
- package/dist/chunk-L3MEMPRH.js +361 -0
- package/dist/chunk-LFTO3Z7N.js +757 -0
- package/dist/chunk-LLZ4I4OR.js +405 -0
- package/dist/chunk-LMRUM4U4.js +207 -0
- package/dist/chunk-LT5OGU6T.js +559 -0
- package/dist/chunk-LZQQOC3M.js +741 -0
- package/dist/chunk-LZYUNF6Q.js +1017 -0
- package/dist/chunk-MCTPVW4G.js +453 -0
- package/dist/chunk-MGLWXBIB.js +65 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-MTE6XDGC.js +541 -0
- package/dist/chunk-NDJUNDAE.js +676 -0
- package/dist/chunk-NG7SNFUD.js +1027 -0
- package/dist/chunk-ONVWKYK7.js +739 -0
- package/dist/chunk-OVMTFGE7.js +1042 -0
- package/dist/chunk-P5RQPRJ4.js +741 -0
- package/dist/chunk-PFABSW6Y.js +401 -0
- package/dist/chunk-PVVUJA2M.js +65 -0
- package/dist/chunk-Q3CDTGTX.js +676 -0
- package/dist/chunk-QKAKWDOQ.js +967 -0
- package/dist/chunk-QMPRDU6I.js +598 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RB6RHB6C.js +254 -0
- package/dist/chunk-RD2K7ODW.js +175 -0
- package/dist/chunk-RHBHJND2.js +582 -0
- package/dist/chunk-RUP6POSE.js +453 -0
- package/dist/chunk-RYNSM23L.js +582 -0
- package/dist/chunk-S72S7XXS.js +255 -0
- package/dist/chunk-SDV5AAPW.js +213 -0
- package/dist/chunk-STXQPXUY.js +242 -0
- package/dist/chunk-TUSF5AEG.js +140 -0
- package/dist/chunk-V74SNTE6.js +736 -0
- package/dist/chunk-VHM5XETC.js +453 -0
- package/dist/chunk-VKQIXLNL.js +273 -0
- package/dist/chunk-VPG23Z7Y.js +545 -0
- package/dist/chunk-VSP234PR.js +627 -0
- package/dist/chunk-WH4JPUWF.js +598 -0
- package/dist/chunk-WLAUJL3K.js +409 -0
- package/dist/chunk-WXCN2QJ7.js +350 -0
- package/dist/chunk-X3JZKLJC.js +896 -0
- package/dist/chunk-X7SAP7FC.js +582 -0
- package/dist/chunk-XEH6PRYE.js +968 -0
- package/dist/chunk-XI65CAQV.js +211 -0
- package/dist/chunk-Y7OSHR6W.js +235 -0
- package/dist/chunk-YN4WUMVD.js +1020 -0
- package/dist/chunk-YUWJ2C4Y.js +1020 -0
- package/dist/chunk-YXLBPWNU.js +263 -0
- package/dist/chunk-YYJJHO7R.js +407 -0
- package/dist/chunk-Z2RBHUIH.js +757 -0
- package/dist/chunk-Z6G3KIOM.js +1027 -0
- package/dist/chunk-ZPPXCDSH.js +361 -0
- package/dist/chunk-ZRTY24SZ.js +582 -0
- package/dist/chunk-ZSDFBCQG.js +741 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +339 -0
- package/dist/esmLoader.d.ts +10 -0
- package/dist/esmLoader.js +112 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +126 -0
- package/dist/memory/memory.selftest.d.ts +2 -0
- package/dist/memory/memory.selftest.js +25 -0
- package/dist/worker/processEntry.d.ts +2 -0
- package/dist/worker/processEntry.js +160 -0
- package/dist/worker/workerEntry.d.ts +2 -0
- package/dist/worker/workerEntry.js +27 -0
- package/native/examples/add.c +6 -0
- package/native/examples/add_test.c +23 -0
- package/native/relaxnative_test.h +36 -0
- package/package.json +81 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// src/worker/workerPool.ts
|
|
2
|
+
import { Worker } from "worker_threads";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
var WORKER_ENTRY_TS_URL = new URL(
|
|
5
|
+
"./workerEntry.vitest.ts",
|
|
6
|
+
import.meta.url
|
|
7
|
+
);
|
|
8
|
+
var WORKER_ENTRY_JS_URL = new URL("./workerEntry.js", import.meta.url);
|
|
9
|
+
var worker = null;
|
|
10
|
+
var seq = 0;
|
|
11
|
+
var pending = /* @__PURE__ */ new Map();
|
|
12
|
+
function getWorker() {
|
|
13
|
+
if (!worker) {
|
|
14
|
+
if (existsSync(WORKER_ENTRY_JS_URL)) {
|
|
15
|
+
worker = new Worker(WORKER_ENTRY_JS_URL, {
|
|
16
|
+
env: { ...process.env }
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
const entryHref = WORKER_ENTRY_TS_URL.href;
|
|
20
|
+
const bootstrap = `import(${JSON.stringify(entryHref)});`;
|
|
21
|
+
worker = new Worker(bootstrap, {
|
|
22
|
+
eval: true,
|
|
23
|
+
env: { ...process.env }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
worker.on("message", (msg) => {
|
|
27
|
+
const resolve = pending.get(msg.id);
|
|
28
|
+
if (resolve) {
|
|
29
|
+
resolve(msg);
|
|
30
|
+
pending.delete(msg.id);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return worker;
|
|
35
|
+
}
|
|
36
|
+
function runInWorker(req) {
|
|
37
|
+
const id = ++seq;
|
|
38
|
+
const w = getWorker();
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
pending.set(id, (res) => {
|
|
41
|
+
if (res.error) {
|
|
42
|
+
const err = new Error(res.error + (res.errorCallsite ? `
|
|
43
|
+
--- remote callsite ---
|
|
44
|
+
${res.errorCallsite}` : ""));
|
|
45
|
+
reject(err);
|
|
46
|
+
} else resolve(res.result);
|
|
47
|
+
});
|
|
48
|
+
w.postMessage({ ...req, id });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/dx/logger.ts
|
|
53
|
+
var enabled = false;
|
|
54
|
+
function isDebugEnabled() {
|
|
55
|
+
return enabled || process.env.RELAXNATIVE_DEBUG === "1";
|
|
56
|
+
}
|
|
57
|
+
function logDebug(...args) {
|
|
58
|
+
if (!isDebugEnabled()) return;
|
|
59
|
+
console.log("[relaxnative]", ...args);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/utils/callsite.ts
|
|
63
|
+
function captureCallsite() {
|
|
64
|
+
const err = new Error();
|
|
65
|
+
if (!err.stack) return void 0;
|
|
66
|
+
const parts = err.stack.split("\n");
|
|
67
|
+
if (parts.length <= 2) return err.stack;
|
|
68
|
+
return parts.slice(2).join("\n");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/worker/workerClient.ts
|
|
72
|
+
function callAsync(libPath, bindings, fn, args) {
|
|
73
|
+
const callsite = captureCallsite();
|
|
74
|
+
logDebug("worker dispatch", { fn, callsite: !!callsite });
|
|
75
|
+
return runInWorker({ libPath, bindings, fn, args, callsite });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/worker/processClient.ts
|
|
79
|
+
import { fork } from "child_process";
|
|
80
|
+
import { fileURLToPath } from "url";
|
|
81
|
+
import { dirname, join } from "path";
|
|
82
|
+
var ProcessIsolationError = class extends Error {
|
|
83
|
+
code;
|
|
84
|
+
details;
|
|
85
|
+
constructor(code, message, details) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.code = code;
|
|
88
|
+
this.details = details;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var child = null;
|
|
92
|
+
var seq2 = 0;
|
|
93
|
+
var pending2 = /* @__PURE__ */ new Map();
|
|
94
|
+
function helperEntryPath() {
|
|
95
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
96
|
+
const pkgRoot = join(here, "..", "..");
|
|
97
|
+
const distEntry = join(pkgRoot, "dist", "worker", "processEntry.js");
|
|
98
|
+
return { distEntry, pkgRoot };
|
|
99
|
+
}
|
|
100
|
+
function startChild() {
|
|
101
|
+
if (child && child.connected) return child;
|
|
102
|
+
const { distEntry } = helperEntryPath();
|
|
103
|
+
const entry = distEntry;
|
|
104
|
+
const cp = fork(entry, {
|
|
105
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
106
|
+
env: {
|
|
107
|
+
...process.env,
|
|
108
|
+
RELAXNATIVE_PROCESS_ISOLATION: "1"
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
cp.on("message", (msg) => {
|
|
112
|
+
if (!msg || typeof msg.id !== "number") return;
|
|
113
|
+
const p = pending2.get(msg.id);
|
|
114
|
+
if (!p) return;
|
|
115
|
+
if (p.cp !== cp) return;
|
|
116
|
+
pending2.delete(msg.id);
|
|
117
|
+
const res = msg;
|
|
118
|
+
if (res.ok) p.resolve(res.result);
|
|
119
|
+
else {
|
|
120
|
+
const details = [res.error.message];
|
|
121
|
+
if (res.error.callsite) details.push("\n--- remote callsite ---\n" + res.error.callsite);
|
|
122
|
+
const err = new Error(details.join("\n"));
|
|
123
|
+
err.name = res.error.name;
|
|
124
|
+
err.stack = res.error.stack;
|
|
125
|
+
p.reject(err);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const rejectAll = (reason) => {
|
|
129
|
+
for (const [id, p] of pending2) {
|
|
130
|
+
if (p.cp !== cp) continue;
|
|
131
|
+
pending2.delete(id);
|
|
132
|
+
p.reject(reason);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
cp.on("exit", (code, signal) => {
|
|
136
|
+
if (child === cp) child = null;
|
|
137
|
+
const reason = new ProcessIsolationError(
|
|
138
|
+
signal ? "ISOLATED_PROCESS_CRASH" : "ISOLATED_PROCESS_EXIT",
|
|
139
|
+
`Isolated runtime exited (code=${code}, signal=${signal ?? "none"})`,
|
|
140
|
+
{ code, signal }
|
|
141
|
+
);
|
|
142
|
+
rejectAll(reason);
|
|
143
|
+
});
|
|
144
|
+
cp.on("error", (err) => {
|
|
145
|
+
const reason = new ProcessIsolationError(
|
|
146
|
+
"ISOLATED_PROCESS_START_FAILED",
|
|
147
|
+
`Failed to start isolated runtime: ${String(err)}`,
|
|
148
|
+
{ err }
|
|
149
|
+
);
|
|
150
|
+
child = null;
|
|
151
|
+
rejectAll(reason);
|
|
152
|
+
});
|
|
153
|
+
child = cp;
|
|
154
|
+
return cp;
|
|
155
|
+
}
|
|
156
|
+
async function ping(cp) {
|
|
157
|
+
const id = ++seq2;
|
|
158
|
+
const req = { id, type: "ping" };
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
pending2.set(id, { resolve, reject, cp });
|
|
161
|
+
cp.send(req);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async function callIsolated(libPath, bindings, fn, args, safety, callsite) {
|
|
165
|
+
const cp = startChild();
|
|
166
|
+
await ping(cp);
|
|
167
|
+
const id = ++seq2;
|
|
168
|
+
const req = {
|
|
169
|
+
id,
|
|
170
|
+
type: "call",
|
|
171
|
+
libPath,
|
|
172
|
+
bindings,
|
|
173
|
+
safety,
|
|
174
|
+
fn,
|
|
175
|
+
args,
|
|
176
|
+
// forward the captured JS callsite for richer crash diagnostics
|
|
177
|
+
...callsite ? { callsite } : {}
|
|
178
|
+
};
|
|
179
|
+
const timeoutMs = safety?.limits?.timeoutMs;
|
|
180
|
+
const baseCall = new Promise((resolve, reject) => {
|
|
181
|
+
let t = null;
|
|
182
|
+
let timedOut = false;
|
|
183
|
+
const wrappedResolve = (v) => {
|
|
184
|
+
if (timedOut) return;
|
|
185
|
+
if (t) clearTimeout(t);
|
|
186
|
+
resolve(v);
|
|
187
|
+
};
|
|
188
|
+
const wrappedReject = (e) => {
|
|
189
|
+
if (timedOut) return;
|
|
190
|
+
if (t) clearTimeout(t);
|
|
191
|
+
reject(e);
|
|
192
|
+
};
|
|
193
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
194
|
+
t = setTimeout(() => {
|
|
195
|
+
timedOut = true;
|
|
196
|
+
pending2.delete(id);
|
|
197
|
+
try {
|
|
198
|
+
cp.kill();
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
wrappedReject(
|
|
202
|
+
new ProcessIsolationError(
|
|
203
|
+
"ISOLATED_PROCESS_CRASH",
|
|
204
|
+
`Isolated runtime exceeded timeout (${timeoutMs}ms)`,
|
|
205
|
+
{ timeoutMs }
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
}, timeoutMs);
|
|
209
|
+
}
|
|
210
|
+
pending2.set(id, { resolve: wrappedResolve, reject: wrappedReject, cp });
|
|
211
|
+
cp.send(req);
|
|
212
|
+
});
|
|
213
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
214
|
+
return Promise.race([
|
|
215
|
+
baseCall,
|
|
216
|
+
new Promise((_, reject) => {
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
reject(
|
|
219
|
+
new ProcessIsolationError(
|
|
220
|
+
"ISOLATED_PROCESS_CRASH",
|
|
221
|
+
`Isolated runtime exceeded timeout (${timeoutMs}ms)`,
|
|
222
|
+
{ timeoutMs }
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}, timeoutMs + 25);
|
|
226
|
+
})
|
|
227
|
+
]);
|
|
228
|
+
}
|
|
229
|
+
return baseCall;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/worker/wrapFunctions.ts
|
|
233
|
+
function wrapFunctions(api, libPath, bindings, opts) {
|
|
234
|
+
const wrapped = {};
|
|
235
|
+
const isolation = opts?.isolation ?? "worker";
|
|
236
|
+
const safety = bindings?.__safety ?? { trust: "local" };
|
|
237
|
+
const bindingNames = Object.keys(bindings?.functions ?? {});
|
|
238
|
+
const apiNames = Object.keys(api ?? {});
|
|
239
|
+
const names = Array.from(/* @__PURE__ */ new Set([...bindingNames, ...apiNames]));
|
|
240
|
+
for (const name of names) {
|
|
241
|
+
const fn = api?.[name];
|
|
242
|
+
const binding = bindings?.functions?.[name];
|
|
243
|
+
const isAsync = binding?.mode === "async" || binding?.async === true || binding?.thread === "worker" || binding?.cost === "high";
|
|
244
|
+
wrapped[name] = (...args) => {
|
|
245
|
+
if (isolation === "process") {
|
|
246
|
+
return callIsolated(libPath, bindings, name, args, safety);
|
|
247
|
+
}
|
|
248
|
+
if (isolation === "worker") {
|
|
249
|
+
if (typeof fn === "function" && !isAsync) {
|
|
250
|
+
return fn(...args);
|
|
251
|
+
}
|
|
252
|
+
return callAsync(libPath, bindings, name, args);
|
|
253
|
+
}
|
|
254
|
+
if (typeof fn !== "function") {
|
|
255
|
+
throw new Error(`Function not found: ${name}`);
|
|
256
|
+
}
|
|
257
|
+
return fn(...args);
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (isolation === "process") {
|
|
261
|
+
wrapped.__call = (fn, ...args) => callIsolated(libPath, bindings, fn, args, safety, captureCallsite());
|
|
262
|
+
return new Proxy(wrapped, {
|
|
263
|
+
get(target, prop) {
|
|
264
|
+
if (typeof prop !== "string") return target[prop];
|
|
265
|
+
if (prop === "then") return void 0;
|
|
266
|
+
if (prop in target) return target[prop];
|
|
267
|
+
return (...args) => callIsolated(libPath, bindings, prop, args, safety, captureCallsite());
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (isolation === "worker") {
|
|
272
|
+
return new Proxy(wrapped, {
|
|
273
|
+
get(target, prop) {
|
|
274
|
+
if (typeof prop !== "string") return target[prop];
|
|
275
|
+
if (prop === "then") return void 0;
|
|
276
|
+
if (prop in target) return target[prop];
|
|
277
|
+
return (...args) => callAsync(libPath, bindings, prop, args);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return wrapped;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export {
|
|
285
|
+
logDebug,
|
|
286
|
+
wrapFunctions
|
|
287
|
+
};
|