env-runner 0.2.3 → 0.3.0
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/README.md +154 -19
- package/dist/_chunks/common-base-runner.d.mts +52 -14
- package/dist/_chunks/common-base-runner.mjs +123 -32
- package/dist/_chunks/common-process-data.mjs +13 -0
- package/dist/_chunks/common-worker-utils.mjs +464 -73
- package/dist/_chunks/deno-process-runner.mjs +2 -6
- package/dist/_chunks/lexer.mjs +2 -0
- package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
- package/dist/_chunks/miniflare-runner.d.mts +21 -12
- package/dist/_chunks/miniflare-runner.mjs +266 -96
- package/dist/_chunks/node-worker-runner.mjs +1 -2
- package/dist/_chunks/server.mjs +20 -5
- package/dist/_chunks/types.d.mts +36 -0
- package/dist/_chunks/virtual-loader.mjs +303 -29
- package/dist/index.d.mts +17 -4
- package/dist/runners/bun-process/runner.mjs +4 -8
- package/dist/runners/bun-process/worker.mjs +8 -8
- package/dist/runners/deno-process/worker.mjs +8 -8
- package/dist/runners/node-process/runner.mjs +2 -6
- package/dist/runners/node-process/worker.mjs +8 -8
- package/dist/runners/node-worker/worker.mjs +6 -7
- package/dist/runners/self/runner.d.mts +2 -1
- package/dist/runners/self/runner.mjs +8 -0
- package/package.json +11 -11
- package/dist/_chunks/common-host-env.mjs +0 -11
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import { resolveVirtualModules } from "./virtual-loader.mjs";
|
|
1
|
+
import { encodeVirtualModules, normalizeVirtualModules, resolveVirtualModules, warnVirtualPathCollisions } from "./virtual-loader.mjs";
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
3
|
import { proxyFetch, proxyUpgrade } from "httpxy";
|
|
4
|
+
function hostEnv(extra) {
|
|
5
|
+
const env = {
|
|
6
|
+
...process.env,
|
|
7
|
+
...extra
|
|
8
|
+
};
|
|
9
|
+
const stdout = process.stdout;
|
|
10
|
+
if (!("FORCE_COLOR" in env) && !env.NO_COLOR && stdout?.isTTY) env.FORCE_COLOR = "1";
|
|
11
|
+
if (!("COLUMNS" in env) && stdout?.columns) env.COLUMNS = String(stdout.columns);
|
|
12
|
+
return env;
|
|
13
|
+
}
|
|
4
14
|
var BaseEnvRunner = class {
|
|
5
15
|
closed = false;
|
|
6
16
|
_name;
|
|
@@ -11,7 +21,11 @@ var BaseEnvRunner = class {
|
|
|
11
21
|
_address;
|
|
12
22
|
_messageListeners;
|
|
13
23
|
_pendingRequests;
|
|
24
|
+
_closeCause;
|
|
14
25
|
_virtualResolved;
|
|
26
|
+
_virtualUpdates = Promise.resolve();
|
|
27
|
+
#virtualUpdateId = 0;
|
|
28
|
+
_processData;
|
|
15
29
|
constructor(opts) {
|
|
16
30
|
this._name = opts.name;
|
|
17
31
|
this._workerEntry = opts.workerEntry;
|
|
@@ -49,24 +63,29 @@ var BaseEnvRunner = class {
|
|
|
49
63
|
}
|
|
50
64
|
waitForReady(timeout = 15e3) {
|
|
51
65
|
if (this.ready) return Promise.resolve();
|
|
52
|
-
if (this.closed) return Promise.reject(
|
|
66
|
+
if (this.closed) return Promise.reject(closedBeforeReadyError(this._closeCause));
|
|
53
67
|
return new Promise((resolve, reject) => {
|
|
54
68
|
const timer = setTimeout(() => {
|
|
55
|
-
|
|
69
|
+
cleanup();
|
|
56
70
|
reject(/* @__PURE__ */ new Error("Runner did not become ready in time"));
|
|
57
71
|
}, timeout);
|
|
58
72
|
const listener = () => {
|
|
59
73
|
if (this.ready) {
|
|
60
|
-
|
|
61
|
-
this._messageListeners.delete(listener);
|
|
74
|
+
cleanup();
|
|
62
75
|
resolve();
|
|
63
|
-
} else if (this.closed) {
|
|
64
|
-
clearTimeout(timer);
|
|
65
|
-
this._messageListeners.delete(listener);
|
|
66
|
-
reject(/* @__PURE__ */ new Error("Runner closed before becoming ready"));
|
|
67
76
|
}
|
|
68
77
|
};
|
|
78
|
+
const onClose = (cause) => {
|
|
79
|
+
cleanup();
|
|
80
|
+
reject(closedBeforeReadyError(cause));
|
|
81
|
+
};
|
|
82
|
+
const cleanup = () => {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
this._messageListeners.delete(listener);
|
|
85
|
+
this._pendingRequests.delete(onClose);
|
|
86
|
+
};
|
|
69
87
|
this._messageListeners.add(listener);
|
|
88
|
+
this._pendingRequests.add(onClose);
|
|
70
89
|
});
|
|
71
90
|
}
|
|
72
91
|
rpc(name, data, opts) {
|
|
@@ -82,27 +101,27 @@ var BaseEnvRunner = class {
|
|
|
82
101
|
}).then((msg) => msg.data);
|
|
83
102
|
}
|
|
84
103
|
async reloadModule(timeout = 5e3) {
|
|
104
|
+
await this._virtualUpdates;
|
|
85
105
|
await this._request({ event: "reload-module" }, {
|
|
86
106
|
match: (msg) => msg?.event === "module-reloaded",
|
|
87
107
|
timeout,
|
|
88
108
|
timeoutError: "Module reload timed out"
|
|
89
109
|
});
|
|
90
110
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
source
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
timeoutError: `Module invalidation timed out for "${specifier}"`
|
|
101
|
-
});
|
|
111
|
+
updateVirtualModules(changes, timeout = 5e3) {
|
|
112
|
+
return this._enqueueVirtualUpdate(() => changes, timeout);
|
|
113
|
+
}
|
|
114
|
+
invalidateModule(specifier, timeout = 5e3) {
|
|
115
|
+
return this._enqueueVirtualUpdate(() => {
|
|
116
|
+
const source = this._virtualSources?.[specifier];
|
|
117
|
+
if (source === void 0) throw new Error(`Cannot invalidate "${specifier}" (not a registered virtual module)`);
|
|
118
|
+
return { [specifier]: source };
|
|
119
|
+
}, timeout);
|
|
102
120
|
}
|
|
103
121
|
async close(cause) {
|
|
104
122
|
if (this.closed) return;
|
|
105
123
|
this.closed = true;
|
|
124
|
+
this._closeCause = cause;
|
|
106
125
|
for (const rejectPending of this._pendingRequests) rejectPending(cause);
|
|
107
126
|
this._pendingRequests.clear();
|
|
108
127
|
this._hooks.onClose?.(this, cause);
|
|
@@ -133,6 +152,36 @@ var BaseEnvRunner = class {
|
|
|
133
152
|
if (message?.event === "init-error" && !this.ready && !this.closed) this.close(new Error(String(message.error || "Worker initialization failed")));
|
|
134
153
|
for (const listener of this._messageListeners) listener(message);
|
|
135
154
|
}
|
|
155
|
+
_processEnv() {
|
|
156
|
+
const data = this._data || {};
|
|
157
|
+
const virtual = data.virtual;
|
|
158
|
+
try {
|
|
159
|
+
this._processData = JSON.stringify(virtual ? {
|
|
160
|
+
...data,
|
|
161
|
+
virtual: encodeVirtualModules(virtual)
|
|
162
|
+
} : data);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new TypeError(`Runner data must be JSON-serializable: ${error?.message || error}`, { cause: error });
|
|
165
|
+
}
|
|
166
|
+
return hostEnv({ ENV_RUNNER_NAME: this._name });
|
|
167
|
+
}
|
|
168
|
+
_handleProcessMessage(message) {
|
|
169
|
+
if (message?.event !== "request-init-data") {
|
|
170
|
+
this._handleMessage(message);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (this.closed) return;
|
|
174
|
+
try {
|
|
175
|
+
this.sendMessage({
|
|
176
|
+
event: "init-data",
|
|
177
|
+
data: this._processData ?? "{}"
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const cause = new Error(`Failed to send runner data to the worker over IPC: ${error?.message || error}`, { cause: error });
|
|
181
|
+
console.error(`[env-runner] ${cause.message}`);
|
|
182
|
+
this.close(cause);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
136
185
|
_request(message, opts) {
|
|
137
186
|
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Runner is closed"));
|
|
138
187
|
return new Promise((resolve, reject) => {
|
|
@@ -168,8 +217,17 @@ var BaseEnvRunner = class {
|
|
|
168
217
|
}
|
|
169
218
|
_resolveVirtualData() {
|
|
170
219
|
const virtual = this._data?.virtual;
|
|
171
|
-
this._virtualSources = virtual;
|
|
172
|
-
|
|
220
|
+
this._virtualSources = { ...virtual };
|
|
221
|
+
this._data = { ...this._data };
|
|
222
|
+
warnVirtualPathCollisions(Object.keys(virtual ?? {}));
|
|
223
|
+
if (!virtual) return;
|
|
224
|
+
if (!Object.values(virtual).some((v) => typeof v === "function")) try {
|
|
225
|
+
this._data.virtual = normalizeVirtualModules(virtual);
|
|
226
|
+
return;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
this._virtualResolved = Promise.reject(error);
|
|
229
|
+
return this._virtualResolved;
|
|
230
|
+
}
|
|
173
231
|
this._virtualResolved = resolveVirtualModules(virtual).then((resolved) => {
|
|
174
232
|
this._data = {
|
|
175
233
|
...this._data,
|
|
@@ -178,20 +236,50 @@ var BaseEnvRunner = class {
|
|
|
178
236
|
});
|
|
179
237
|
return this._virtualResolved;
|
|
180
238
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
239
|
+
_enqueueVirtualUpdate(changes, timeout) {
|
|
240
|
+
const update = this._virtualUpdates.then(async () => {
|
|
241
|
+
await this._virtualResolved;
|
|
242
|
+
if (this.closed) throw new Error("Runner is closed");
|
|
243
|
+
const entries = Object.entries(changes()).filter(([, source]) => source !== void 0);
|
|
244
|
+
if (entries.length === 0) return;
|
|
245
|
+
const sets = Object.fromEntries(entries.filter((entry) => entry[1] !== null));
|
|
246
|
+
const resolved = await resolveVirtualModules(sets);
|
|
247
|
+
const sources = this._virtualSources ??= {};
|
|
248
|
+
const virtual = (this._data ??= {}).virtual ??= {};
|
|
249
|
+
let added = false;
|
|
250
|
+
for (const [key, source] of entries) if (source === null) {
|
|
251
|
+
resolved[key] = null;
|
|
252
|
+
delete sources[key];
|
|
253
|
+
delete virtual[key];
|
|
254
|
+
} else {
|
|
255
|
+
added ||= !Object.hasOwn(sources, key);
|
|
256
|
+
sources[key] = source;
|
|
257
|
+
virtual[key] = resolved[key];
|
|
258
|
+
}
|
|
259
|
+
if (added) warnVirtualPathCollisions(Object.keys(virtual));
|
|
260
|
+
if (!this.ready) await this.waitForReady();
|
|
261
|
+
await this._applyVirtualUpdates(resolved, timeout);
|
|
262
|
+
});
|
|
263
|
+
this._virtualUpdates = update.catch(() => {});
|
|
264
|
+
return update;
|
|
265
|
+
}
|
|
266
|
+
async _applyVirtualUpdates(changes, timeout) {
|
|
267
|
+
const id = ++this.#virtualUpdateId;
|
|
268
|
+
await this._request({
|
|
269
|
+
event: "update-virtual-modules",
|
|
270
|
+
id,
|
|
271
|
+
changes: encodeVirtualModules(changes)
|
|
272
|
+
}, {
|
|
273
|
+
match: (msg) => msg?.event === "virtual-modules-updated" && msg.id === id,
|
|
274
|
+
timeout,
|
|
275
|
+
timeoutError: "Virtual module update timed out"
|
|
276
|
+
});
|
|
189
277
|
}
|
|
190
278
|
_initWithVirtualData(init) {
|
|
191
279
|
const pending = this._resolveVirtualData();
|
|
192
280
|
if (pending) pending.then(() => {
|
|
193
281
|
if (!this.closed) init();
|
|
194
|
-
}
|
|
282
|
+
}).catch((error) => this.close(error));
|
|
195
283
|
else init();
|
|
196
284
|
}
|
|
197
285
|
async _closeSocket() {
|
|
@@ -200,4 +288,7 @@ var BaseEnvRunner = class {
|
|
|
200
288
|
this._address = void 0;
|
|
201
289
|
}
|
|
202
290
|
};
|
|
203
|
-
|
|
291
|
+
function closedBeforeReadyError(cause) {
|
|
292
|
+
return new Error("Runner closed before becoming ready", cause ? { cause } : void 0);
|
|
293
|
+
}
|
|
294
|
+
export { BaseEnvRunner, hostEnv };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function receiveProcessData() {
|
|
2
|
+
return new Promise((resolve) => {
|
|
3
|
+
const onMessage = (message) => {
|
|
4
|
+
if (message?.event === "init-data") {
|
|
5
|
+
process.off("message", onMessage);
|
|
6
|
+
resolve(message.data);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
process.on("message", onMessage);
|
|
10
|
+
process.send({ event: "request-init-data" });
|
|
11
|
+
}).then((json) => JSON.parse(json));
|
|
12
|
+
}
|
|
13
|
+
export { receiveProcessData };
|