@ricsam/isolate-server 0.0.1 → 0.2.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 +53 -43
- package/dist/cjs/index.cjs +210 -0
- package/dist/cjs/index.cjs.map +10 -0
- package/dist/cjs/package.json +5 -0
- package/dist/mjs/index.mjs +180 -0
- package/dist/mjs/index.mjs.map +10 -0
- package/dist/mjs/package.json +5 -0
- package/dist/types/index.d.ts +42 -0
- package/package.json +48 -6
package/README.md
CHANGED
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
# @ricsam/isolate-server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
3
|
+
Reusable server lifecycle manager for `@ricsam/isolate-client` runtimes.
|
|
4
|
+
|
|
5
|
+
`IsolateServer` wraps namespaced runtime lifecycle (`start`, `reload`, `close`) and provides a stable
|
|
6
|
+
`fetch` proxy that can auto-start from the last successful configuration.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm add @ricsam/isolate-server
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { IsolateServer } from "@ricsam/isolate-server";
|
|
18
|
+
import { connect } from "@ricsam/isolate-client";
|
|
19
|
+
|
|
20
|
+
const connection = await connect({ socket: "/tmp/isolate.sock" });
|
|
21
|
+
|
|
22
|
+
const server = new IsolateServer({
|
|
23
|
+
namespaceId: "project/main",
|
|
24
|
+
getConnection: async () => connection,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await server.start({
|
|
28
|
+
entry: "server.js",
|
|
29
|
+
runtimeOptions: {
|
|
30
|
+
moduleLoader: (specifier) => {
|
|
31
|
+
if (specifier === "server.js") {
|
|
32
|
+
return {
|
|
33
|
+
code: `serve({ fetch: () => new Response("ok") });`,
|
|
34
|
+
resolveDir: "/",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Unknown module: ${specifier}`);
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const response = await server.fetch.dispatchRequest(new Request("http://localhost/"));
|
|
43
|
+
console.log(await response.text()); // "ok"
|
|
44
|
+
|
|
45
|
+
await server.reload();
|
|
46
|
+
await server.close();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
- `start(options)` configures and starts the runtime (idempotent when already running).
|
|
52
|
+
- `reload()` disposes current runtime and starts again with the last start options.
|
|
53
|
+
- `close()` disposes current runtime (idempotent).
|
|
54
|
+
- `getRuntime()` returns the active runtime or `null`.
|
|
55
|
+
- `fetch.*` proxies to runtime fetch methods and auto-starts after an initial `start()`.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
+
var __toCommonJS = (from) => {
|
|
7
|
+
var entry = __moduleCache.get(from), desc;
|
|
8
|
+
if (entry)
|
|
9
|
+
return entry;
|
|
10
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
}));
|
|
16
|
+
__moduleCache.set(from, entry);
|
|
17
|
+
return entry;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// packages/isolate-server/src/index.ts
|
|
30
|
+
var exports_src = {};
|
|
31
|
+
__export(exports_src, {
|
|
32
|
+
IsolateServer: () => IsolateServer
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(exports_src);
|
|
35
|
+
var import_isolate_client = require("@ricsam/isolate-client");
|
|
36
|
+
var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
|
|
37
|
+
function isLinkerConflictError(error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
39
|
+
return message.includes(LINKER_CONFLICT_ERROR);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class IsolateServer {
|
|
43
|
+
namespaceId;
|
|
44
|
+
getConnection;
|
|
45
|
+
runtime = null;
|
|
46
|
+
lastStartOptions = null;
|
|
47
|
+
lifecycleLock = Promise.resolve();
|
|
48
|
+
closed = true;
|
|
49
|
+
fetch = {
|
|
50
|
+
dispatchRequest: (request, options) => this.dispatchRequestWithRetry(request, options),
|
|
51
|
+
getUpgradeRequest: async () => {
|
|
52
|
+
const runtime = await this.getActiveRuntime();
|
|
53
|
+
return runtime.fetch.getUpgradeRequest();
|
|
54
|
+
},
|
|
55
|
+
dispatchWebSocketOpen: async (connectionId) => {
|
|
56
|
+
const runtime = await this.getActiveRuntime();
|
|
57
|
+
await runtime.fetch.dispatchWebSocketOpen(connectionId);
|
|
58
|
+
},
|
|
59
|
+
dispatchWebSocketMessage: async (connectionId, message) => {
|
|
60
|
+
const runtime = await this.getActiveRuntime();
|
|
61
|
+
await runtime.fetch.dispatchWebSocketMessage(connectionId, message);
|
|
62
|
+
},
|
|
63
|
+
dispatchWebSocketClose: async (connectionId, code, reason) => {
|
|
64
|
+
const runtime = await this.getActiveRuntime();
|
|
65
|
+
await runtime.fetch.dispatchWebSocketClose(connectionId, code, reason);
|
|
66
|
+
},
|
|
67
|
+
dispatchWebSocketError: async (connectionId, error) => {
|
|
68
|
+
const runtime = await this.getActiveRuntime();
|
|
69
|
+
await runtime.fetch.dispatchWebSocketError(connectionId, error);
|
|
70
|
+
},
|
|
71
|
+
hasServeHandler: async () => {
|
|
72
|
+
const runtime = await this.getActiveRuntime();
|
|
73
|
+
return runtime.fetch.hasServeHandler();
|
|
74
|
+
},
|
|
75
|
+
hasActiveConnections: async () => {
|
|
76
|
+
const runtime = await this.getActiveRuntime();
|
|
77
|
+
return runtime.fetch.hasActiveConnections();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
constructor(options) {
|
|
81
|
+
this.namespaceId = options.namespaceId;
|
|
82
|
+
this.getConnection = options.getConnection;
|
|
83
|
+
}
|
|
84
|
+
async start(options) {
|
|
85
|
+
this.lastStartOptions = options;
|
|
86
|
+
this.closed = false;
|
|
87
|
+
await this.withLifecycleLock(async () => {
|
|
88
|
+
if (this.runtime) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.runtime = await this.createAndInitializeRuntime(options);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async reload() {
|
|
95
|
+
const startOptions = this.lastStartOptions;
|
|
96
|
+
if (!startOptions) {
|
|
97
|
+
throw new Error("Server not configured. Call start() first.");
|
|
98
|
+
}
|
|
99
|
+
this.closed = false;
|
|
100
|
+
await this.withLifecycleLock(async () => {
|
|
101
|
+
if (this.runtime) {
|
|
102
|
+
const runtime = this.runtime;
|
|
103
|
+
this.runtime = null;
|
|
104
|
+
await this.disposeRuntime(runtime);
|
|
105
|
+
}
|
|
106
|
+
this.runtime = await this.createAndInitializeRuntime(startOptions);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async close() {
|
|
110
|
+
await this.withLifecycleLock(async () => {
|
|
111
|
+
if (this.runtime) {
|
|
112
|
+
const runtime = this.runtime;
|
|
113
|
+
this.runtime = null;
|
|
114
|
+
await this.disposeRuntime(runtime);
|
|
115
|
+
}
|
|
116
|
+
this.closed = true;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
getRuntime() {
|
|
120
|
+
return this.runtime;
|
|
121
|
+
}
|
|
122
|
+
async withLifecycleLock(operation) {
|
|
123
|
+
const previous = this.lifecycleLock;
|
|
124
|
+
let release;
|
|
125
|
+
this.lifecycleLock = new Promise((resolve) => {
|
|
126
|
+
release = resolve;
|
|
127
|
+
});
|
|
128
|
+
await previous;
|
|
129
|
+
try {
|
|
130
|
+
return await operation();
|
|
131
|
+
} finally {
|
|
132
|
+
release();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
buildRuntimeOptions(options) {
|
|
136
|
+
if (options.onWebSocketCommand) {
|
|
137
|
+
return {
|
|
138
|
+
...options.runtimeOptions,
|
|
139
|
+
onWebSocketCommand: options.onWebSocketCommand
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return options.runtimeOptions;
|
|
143
|
+
}
|
|
144
|
+
async createAndInitializeRuntime(options, allowRetry = true) {
|
|
145
|
+
const connection = await this.getConnection();
|
|
146
|
+
const namespace = connection.createNamespace(this.namespaceId);
|
|
147
|
+
const runtimeOptions = this.buildRuntimeOptions(options);
|
|
148
|
+
const runtime = await namespace.createRuntime(runtimeOptions);
|
|
149
|
+
try {
|
|
150
|
+
await runtime.eval(`import ${JSON.stringify(options.entry)};`, options.entryFilename ?? "/isolate_server_entry.js");
|
|
151
|
+
return runtime;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
await this.disposeRuntime(runtime);
|
|
154
|
+
if (!allowRetry || !isLinkerConflictError(error)) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
const retryRuntime = await namespace.createRuntime(runtimeOptions);
|
|
158
|
+
try {
|
|
159
|
+
await retryRuntime.eval(`import ${JSON.stringify(options.entry)};`, options.entryFilename ?? "/isolate_server_entry.js");
|
|
160
|
+
return retryRuntime;
|
|
161
|
+
} catch (retryError) {
|
|
162
|
+
await this.disposeRuntime(retryRuntime);
|
|
163
|
+
throw retryError;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async disposeRuntime(runtime) {
|
|
168
|
+
try {
|
|
169
|
+
await runtime.dispose();
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (!import_isolate_client.isBenignDisposeError(error)) {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async ensureStarted() {
|
|
177
|
+
if (this.runtime) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (!this.lastStartOptions) {
|
|
181
|
+
throw new Error("Server not configured. Call start() first.");
|
|
182
|
+
}
|
|
183
|
+
if (this.closed) {
|
|
184
|
+
this.closed = false;
|
|
185
|
+
}
|
|
186
|
+
await this.start(this.lastStartOptions);
|
|
187
|
+
}
|
|
188
|
+
async getActiveRuntime() {
|
|
189
|
+
await this.ensureStarted();
|
|
190
|
+
if (!this.runtime) {
|
|
191
|
+
throw new Error("Server runtime failed to start.");
|
|
192
|
+
}
|
|
193
|
+
return this.runtime;
|
|
194
|
+
}
|
|
195
|
+
async dispatchRequestWithRetry(request, options) {
|
|
196
|
+
const runtime = await this.getActiveRuntime();
|
|
197
|
+
try {
|
|
198
|
+
return await runtime.fetch.dispatchRequest(request, options);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (!isLinkerConflictError(error)) {
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
await this.reload();
|
|
204
|
+
const retryRuntime = await this.getActiveRuntime();
|
|
205
|
+
return retryRuntime.fetch.dispatchRequest(request, options);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
//# debugId=D8CA16D612F9756664756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n DaemonConnection,\n DispatchOptions,\n RemoteRuntime,\n RuntimeOptions,\n UpgradeRequest,\n WebSocketCommand,\n} from \"@ricsam/isolate-client\";\nimport { isBenignDisposeError } from \"@ricsam/isolate-client\";\n\nconst LINKER_CONFLICT_ERROR = \"Module is currently being linked by another linker\";\n\nfunction isLinkerConflictError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return message.includes(LINKER_CONFLICT_ERROR);\n}\n\nexport interface IsolateServerOptions {\n namespaceId: string;\n getConnection: () => Promise<DaemonConnection>;\n}\n\nexport interface IsolateServerStartOptions {\n runtimeOptions: RuntimeOptions;\n entry: string;\n entryFilename?: string;\n onWebSocketCommand?: (cmd: WebSocketCommand) => void;\n}\n\nexport interface IsolateServerFetch {\n dispatchRequest(request: Request, options?: DispatchOptions): Promise<Response>;\n getUpgradeRequest(): Promise<UpgradeRequest | null>;\n dispatchWebSocketOpen(connectionId: string): Promise<void>;\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): Promise<void>;\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): Promise<void>;\n dispatchWebSocketError(connectionId: string, error: Error): Promise<void>;\n hasServeHandler(): Promise<boolean>;\n hasActiveConnections(): Promise<boolean>;\n}\n\nexport class IsolateServer {\n private readonly namespaceId: string;\n private readonly getConnection: () => Promise<DaemonConnection>;\n private runtime: RemoteRuntime | null = null;\n private lastStartOptions: IsolateServerStartOptions | null = null;\n private lifecycleLock: Promise<void> = Promise.resolve();\n private closed = true;\n\n readonly fetch: IsolateServerFetch = {\n dispatchRequest: (request, options) => this.dispatchRequestWithRetry(request, options),\n getUpgradeRequest: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.getUpgradeRequest();\n },\n dispatchWebSocketOpen: async (connectionId) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage: async (connectionId, message) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose: async (connectionId, code, reason) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError: async (connectionId, error) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketError(connectionId, error);\n },\n hasServeHandler: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.hasServeHandler();\n },\n hasActiveConnections: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.hasActiveConnections();\n },\n };\n\n constructor(options: IsolateServerOptions) {\n this.namespaceId = options.namespaceId;\n this.getConnection = options.getConnection;\n }\n\n async start(options: IsolateServerStartOptions): Promise<void> {\n this.lastStartOptions = options;\n this.closed = false;\n\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n return;\n }\n\n this.runtime = await this.createAndInitializeRuntime(options);\n });\n }\n\n async reload(): Promise<void> {\n const startOptions = this.lastStartOptions;\n if (!startOptions) {\n throw new Error(\"Server not configured. Call start() first.\");\n }\n\n this.closed = false;\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n const runtime = this.runtime;\n this.runtime = null;\n await this.disposeRuntime(runtime);\n }\n\n this.runtime = await this.createAndInitializeRuntime(startOptions);\n });\n }\n\n async close(): Promise<void> {\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n const runtime = this.runtime;\n this.runtime = null;\n await this.disposeRuntime(runtime);\n }\n\n this.closed = true;\n });\n }\n\n getRuntime(): RemoteRuntime | null {\n return this.runtime;\n }\n\n private async withLifecycleLock<T>(operation: () => Promise<T>): Promise<T> {\n const previous = this.lifecycleLock;\n let release!: () => void;\n this.lifecycleLock = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous;\n try {\n return await operation();\n } finally {\n release();\n }\n }\n\n private buildRuntimeOptions(options: IsolateServerStartOptions): RuntimeOptions {\n if (options.onWebSocketCommand) {\n return {\n ...options.runtimeOptions,\n onWebSocketCommand: options.onWebSocketCommand,\n };\n }\n\n return options.runtimeOptions;\n }\n\n private async createAndInitializeRuntime(\n options: IsolateServerStartOptions,\n allowRetry: boolean = true\n ): Promise<RemoteRuntime> {\n const connection = await this.getConnection();\n const namespace = connection.createNamespace(this.namespaceId);\n const runtimeOptions = this.buildRuntimeOptions(options);\n const runtime = await namespace.createRuntime(runtimeOptions);\n\n try {\n await runtime.eval(\n `import ${JSON.stringify(options.entry)};`,\n options.entryFilename ?? \"/isolate_server_entry.js\"\n );\n return runtime;\n } catch (error) {\n await this.disposeRuntime(runtime);\n if (!allowRetry || !isLinkerConflictError(error)) {\n throw error;\n }\n\n const retryRuntime = await namespace.createRuntime(runtimeOptions);\n try {\n await retryRuntime.eval(\n `import ${JSON.stringify(options.entry)};`,\n options.entryFilename ?? \"/isolate_server_entry.js\"\n );\n return retryRuntime;\n } catch (retryError) {\n await this.disposeRuntime(retryRuntime);\n throw retryError;\n }\n }\n }\n\n private async disposeRuntime(runtime: RemoteRuntime): Promise<void> {\n try {\n await runtime.dispose();\n } catch (error) {\n if (!isBenignDisposeError(error)) {\n throw error;\n }\n }\n }\n\n private async ensureStarted(): Promise<void> {\n if (this.runtime) {\n return;\n }\n\n if (!this.lastStartOptions) {\n throw new Error(\"Server not configured. Call start() first.\");\n }\n\n if (this.closed) {\n this.closed = false;\n }\n\n await this.start(this.lastStartOptions);\n }\n\n private async getActiveRuntime(): Promise<RemoteRuntime> {\n await this.ensureStarted();\n if (!this.runtime) {\n throw new Error(\"Server runtime failed to start.\");\n }\n return this.runtime;\n }\n\n private async dispatchRequestWithRetry(\n request: Request,\n options?: DispatchOptions\n ): Promise<Response> {\n const runtime = await this.getActiveRuntime();\n try {\n return await runtime.fetch.dispatchRequest(request, options);\n } catch (error) {\n if (!isLinkerConflictError(error)) {\n throw error;\n }\n\n await this.reload();\n const retryRuntime = await this.getActiveRuntime();\n return retryRuntime.fetch.dispatchRequest(request, options);\n }\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D8CA16D612F9756664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// packages/isolate-server/src/index.ts
|
|
2
|
+
import { isBenignDisposeError } from "@ricsam/isolate-client";
|
|
3
|
+
var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
|
|
4
|
+
function isLinkerConflictError(error) {
|
|
5
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
6
|
+
return message.includes(LINKER_CONFLICT_ERROR);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class IsolateServer {
|
|
10
|
+
namespaceId;
|
|
11
|
+
getConnection;
|
|
12
|
+
runtime = null;
|
|
13
|
+
lastStartOptions = null;
|
|
14
|
+
lifecycleLock = Promise.resolve();
|
|
15
|
+
closed = true;
|
|
16
|
+
fetch = {
|
|
17
|
+
dispatchRequest: (request, options) => this.dispatchRequestWithRetry(request, options),
|
|
18
|
+
getUpgradeRequest: async () => {
|
|
19
|
+
const runtime = await this.getActiveRuntime();
|
|
20
|
+
return runtime.fetch.getUpgradeRequest();
|
|
21
|
+
},
|
|
22
|
+
dispatchWebSocketOpen: async (connectionId) => {
|
|
23
|
+
const runtime = await this.getActiveRuntime();
|
|
24
|
+
await runtime.fetch.dispatchWebSocketOpen(connectionId);
|
|
25
|
+
},
|
|
26
|
+
dispatchWebSocketMessage: async (connectionId, message) => {
|
|
27
|
+
const runtime = await this.getActiveRuntime();
|
|
28
|
+
await runtime.fetch.dispatchWebSocketMessage(connectionId, message);
|
|
29
|
+
},
|
|
30
|
+
dispatchWebSocketClose: async (connectionId, code, reason) => {
|
|
31
|
+
const runtime = await this.getActiveRuntime();
|
|
32
|
+
await runtime.fetch.dispatchWebSocketClose(connectionId, code, reason);
|
|
33
|
+
},
|
|
34
|
+
dispatchWebSocketError: async (connectionId, error) => {
|
|
35
|
+
const runtime = await this.getActiveRuntime();
|
|
36
|
+
await runtime.fetch.dispatchWebSocketError(connectionId, error);
|
|
37
|
+
},
|
|
38
|
+
hasServeHandler: async () => {
|
|
39
|
+
const runtime = await this.getActiveRuntime();
|
|
40
|
+
return runtime.fetch.hasServeHandler();
|
|
41
|
+
},
|
|
42
|
+
hasActiveConnections: async () => {
|
|
43
|
+
const runtime = await this.getActiveRuntime();
|
|
44
|
+
return runtime.fetch.hasActiveConnections();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.namespaceId = options.namespaceId;
|
|
49
|
+
this.getConnection = options.getConnection;
|
|
50
|
+
}
|
|
51
|
+
async start(options) {
|
|
52
|
+
this.lastStartOptions = options;
|
|
53
|
+
this.closed = false;
|
|
54
|
+
await this.withLifecycleLock(async () => {
|
|
55
|
+
if (this.runtime) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.runtime = await this.createAndInitializeRuntime(options);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async reload() {
|
|
62
|
+
const startOptions = this.lastStartOptions;
|
|
63
|
+
if (!startOptions) {
|
|
64
|
+
throw new Error("Server not configured. Call start() first.");
|
|
65
|
+
}
|
|
66
|
+
this.closed = false;
|
|
67
|
+
await this.withLifecycleLock(async () => {
|
|
68
|
+
if (this.runtime) {
|
|
69
|
+
const runtime = this.runtime;
|
|
70
|
+
this.runtime = null;
|
|
71
|
+
await this.disposeRuntime(runtime);
|
|
72
|
+
}
|
|
73
|
+
this.runtime = await this.createAndInitializeRuntime(startOptions);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async close() {
|
|
77
|
+
await this.withLifecycleLock(async () => {
|
|
78
|
+
if (this.runtime) {
|
|
79
|
+
const runtime = this.runtime;
|
|
80
|
+
this.runtime = null;
|
|
81
|
+
await this.disposeRuntime(runtime);
|
|
82
|
+
}
|
|
83
|
+
this.closed = true;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
getRuntime() {
|
|
87
|
+
return this.runtime;
|
|
88
|
+
}
|
|
89
|
+
async withLifecycleLock(operation) {
|
|
90
|
+
const previous = this.lifecycleLock;
|
|
91
|
+
let release;
|
|
92
|
+
this.lifecycleLock = new Promise((resolve) => {
|
|
93
|
+
release = resolve;
|
|
94
|
+
});
|
|
95
|
+
await previous;
|
|
96
|
+
try {
|
|
97
|
+
return await operation();
|
|
98
|
+
} finally {
|
|
99
|
+
release();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
buildRuntimeOptions(options) {
|
|
103
|
+
if (options.onWebSocketCommand) {
|
|
104
|
+
return {
|
|
105
|
+
...options.runtimeOptions,
|
|
106
|
+
onWebSocketCommand: options.onWebSocketCommand
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return options.runtimeOptions;
|
|
110
|
+
}
|
|
111
|
+
async createAndInitializeRuntime(options, allowRetry = true) {
|
|
112
|
+
const connection = await this.getConnection();
|
|
113
|
+
const namespace = connection.createNamespace(this.namespaceId);
|
|
114
|
+
const runtimeOptions = this.buildRuntimeOptions(options);
|
|
115
|
+
const runtime = await namespace.createRuntime(runtimeOptions);
|
|
116
|
+
try {
|
|
117
|
+
await runtime.eval(`import ${JSON.stringify(options.entry)};`, options.entryFilename ?? "/isolate_server_entry.js");
|
|
118
|
+
return runtime;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
await this.disposeRuntime(runtime);
|
|
121
|
+
if (!allowRetry || !isLinkerConflictError(error)) {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
const retryRuntime = await namespace.createRuntime(runtimeOptions);
|
|
125
|
+
try {
|
|
126
|
+
await retryRuntime.eval(`import ${JSON.stringify(options.entry)};`, options.entryFilename ?? "/isolate_server_entry.js");
|
|
127
|
+
return retryRuntime;
|
|
128
|
+
} catch (retryError) {
|
|
129
|
+
await this.disposeRuntime(retryRuntime);
|
|
130
|
+
throw retryError;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async disposeRuntime(runtime) {
|
|
135
|
+
try {
|
|
136
|
+
await runtime.dispose();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (!isBenignDisposeError(error)) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async ensureStarted() {
|
|
144
|
+
if (this.runtime) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!this.lastStartOptions) {
|
|
148
|
+
throw new Error("Server not configured. Call start() first.");
|
|
149
|
+
}
|
|
150
|
+
if (this.closed) {
|
|
151
|
+
this.closed = false;
|
|
152
|
+
}
|
|
153
|
+
await this.start(this.lastStartOptions);
|
|
154
|
+
}
|
|
155
|
+
async getActiveRuntime() {
|
|
156
|
+
await this.ensureStarted();
|
|
157
|
+
if (!this.runtime) {
|
|
158
|
+
throw new Error("Server runtime failed to start.");
|
|
159
|
+
}
|
|
160
|
+
return this.runtime;
|
|
161
|
+
}
|
|
162
|
+
async dispatchRequestWithRetry(request, options) {
|
|
163
|
+
const runtime = await this.getActiveRuntime();
|
|
164
|
+
try {
|
|
165
|
+
return await runtime.fetch.dispatchRequest(request, options);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (!isLinkerConflictError(error)) {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
await this.reload();
|
|
171
|
+
const retryRuntime = await this.getActiveRuntime();
|
|
172
|
+
return retryRuntime.fetch.dispatchRequest(request, options);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
IsolateServer
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
//# debugId=25E2FF8F088B596A64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n DaemonConnection,\n DispatchOptions,\n RemoteRuntime,\n RuntimeOptions,\n UpgradeRequest,\n WebSocketCommand,\n} from \"@ricsam/isolate-client\";\nimport { isBenignDisposeError } from \"@ricsam/isolate-client\";\n\nconst LINKER_CONFLICT_ERROR = \"Module is currently being linked by another linker\";\n\nfunction isLinkerConflictError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return message.includes(LINKER_CONFLICT_ERROR);\n}\n\nexport interface IsolateServerOptions {\n namespaceId: string;\n getConnection: () => Promise<DaemonConnection>;\n}\n\nexport interface IsolateServerStartOptions {\n runtimeOptions: RuntimeOptions;\n entry: string;\n entryFilename?: string;\n onWebSocketCommand?: (cmd: WebSocketCommand) => void;\n}\n\nexport interface IsolateServerFetch {\n dispatchRequest(request: Request, options?: DispatchOptions): Promise<Response>;\n getUpgradeRequest(): Promise<UpgradeRequest | null>;\n dispatchWebSocketOpen(connectionId: string): Promise<void>;\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): Promise<void>;\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): Promise<void>;\n dispatchWebSocketError(connectionId: string, error: Error): Promise<void>;\n hasServeHandler(): Promise<boolean>;\n hasActiveConnections(): Promise<boolean>;\n}\n\nexport class IsolateServer {\n private readonly namespaceId: string;\n private readonly getConnection: () => Promise<DaemonConnection>;\n private runtime: RemoteRuntime | null = null;\n private lastStartOptions: IsolateServerStartOptions | null = null;\n private lifecycleLock: Promise<void> = Promise.resolve();\n private closed = true;\n\n readonly fetch: IsolateServerFetch = {\n dispatchRequest: (request, options) => this.dispatchRequestWithRetry(request, options),\n getUpgradeRequest: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.getUpgradeRequest();\n },\n dispatchWebSocketOpen: async (connectionId) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage: async (connectionId, message) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose: async (connectionId, code, reason) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError: async (connectionId, error) => {\n const runtime = await this.getActiveRuntime();\n await runtime.fetch.dispatchWebSocketError(connectionId, error);\n },\n hasServeHandler: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.hasServeHandler();\n },\n hasActiveConnections: async () => {\n const runtime = await this.getActiveRuntime();\n return runtime.fetch.hasActiveConnections();\n },\n };\n\n constructor(options: IsolateServerOptions) {\n this.namespaceId = options.namespaceId;\n this.getConnection = options.getConnection;\n }\n\n async start(options: IsolateServerStartOptions): Promise<void> {\n this.lastStartOptions = options;\n this.closed = false;\n\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n return;\n }\n\n this.runtime = await this.createAndInitializeRuntime(options);\n });\n }\n\n async reload(): Promise<void> {\n const startOptions = this.lastStartOptions;\n if (!startOptions) {\n throw new Error(\"Server not configured. Call start() first.\");\n }\n\n this.closed = false;\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n const runtime = this.runtime;\n this.runtime = null;\n await this.disposeRuntime(runtime);\n }\n\n this.runtime = await this.createAndInitializeRuntime(startOptions);\n });\n }\n\n async close(): Promise<void> {\n await this.withLifecycleLock(async () => {\n if (this.runtime) {\n const runtime = this.runtime;\n this.runtime = null;\n await this.disposeRuntime(runtime);\n }\n\n this.closed = true;\n });\n }\n\n getRuntime(): RemoteRuntime | null {\n return this.runtime;\n }\n\n private async withLifecycleLock<T>(operation: () => Promise<T>): Promise<T> {\n const previous = this.lifecycleLock;\n let release!: () => void;\n this.lifecycleLock = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous;\n try {\n return await operation();\n } finally {\n release();\n }\n }\n\n private buildRuntimeOptions(options: IsolateServerStartOptions): RuntimeOptions {\n if (options.onWebSocketCommand) {\n return {\n ...options.runtimeOptions,\n onWebSocketCommand: options.onWebSocketCommand,\n };\n }\n\n return options.runtimeOptions;\n }\n\n private async createAndInitializeRuntime(\n options: IsolateServerStartOptions,\n allowRetry: boolean = true\n ): Promise<RemoteRuntime> {\n const connection = await this.getConnection();\n const namespace = connection.createNamespace(this.namespaceId);\n const runtimeOptions = this.buildRuntimeOptions(options);\n const runtime = await namespace.createRuntime(runtimeOptions);\n\n try {\n await runtime.eval(\n `import ${JSON.stringify(options.entry)};`,\n options.entryFilename ?? \"/isolate_server_entry.js\"\n );\n return runtime;\n } catch (error) {\n await this.disposeRuntime(runtime);\n if (!allowRetry || !isLinkerConflictError(error)) {\n throw error;\n }\n\n const retryRuntime = await namespace.createRuntime(runtimeOptions);\n try {\n await retryRuntime.eval(\n `import ${JSON.stringify(options.entry)};`,\n options.entryFilename ?? \"/isolate_server_entry.js\"\n );\n return retryRuntime;\n } catch (retryError) {\n await this.disposeRuntime(retryRuntime);\n throw retryError;\n }\n }\n }\n\n private async disposeRuntime(runtime: RemoteRuntime): Promise<void> {\n try {\n await runtime.dispose();\n } catch (error) {\n if (!isBenignDisposeError(error)) {\n throw error;\n }\n }\n }\n\n private async ensureStarted(): Promise<void> {\n if (this.runtime) {\n return;\n }\n\n if (!this.lastStartOptions) {\n throw new Error(\"Server not configured. Call start() first.\");\n }\n\n if (this.closed) {\n this.closed = false;\n }\n\n await this.start(this.lastStartOptions);\n }\n\n private async getActiveRuntime(): Promise<RemoteRuntime> {\n await this.ensureStarted();\n if (!this.runtime) {\n throw new Error(\"Server runtime failed to start.\");\n }\n return this.runtime;\n }\n\n private async dispatchRequestWithRetry(\n request: Request,\n options?: DispatchOptions\n ): Promise<Response> {\n const runtime = await this.getActiveRuntime();\n try {\n return await runtime.fetch.dispatchRequest(request, options);\n } catch (error) {\n if (!isLinkerConflictError(error)) {\n throw error;\n }\n\n await this.reload();\n const retryRuntime = await this.getActiveRuntime();\n return retryRuntime.fetch.dispatchRequest(request, options);\n }\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "25E2FF8F088B596A64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DaemonConnection, DispatchOptions, RemoteRuntime, RuntimeOptions, UpgradeRequest, WebSocketCommand } from "@ricsam/isolate-client";
|
|
2
|
+
export interface IsolateServerOptions {
|
|
3
|
+
namespaceId: string;
|
|
4
|
+
getConnection: () => Promise<DaemonConnection>;
|
|
5
|
+
}
|
|
6
|
+
export interface IsolateServerStartOptions {
|
|
7
|
+
runtimeOptions: RuntimeOptions;
|
|
8
|
+
entry: string;
|
|
9
|
+
entryFilename?: string;
|
|
10
|
+
onWebSocketCommand?: (cmd: WebSocketCommand) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface IsolateServerFetch {
|
|
13
|
+
dispatchRequest(request: Request, options?: DispatchOptions): Promise<Response>;
|
|
14
|
+
getUpgradeRequest(): Promise<UpgradeRequest | null>;
|
|
15
|
+
dispatchWebSocketOpen(connectionId: string): Promise<void>;
|
|
16
|
+
dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): Promise<void>;
|
|
17
|
+
dispatchWebSocketClose(connectionId: string, code: number, reason: string): Promise<void>;
|
|
18
|
+
dispatchWebSocketError(connectionId: string, error: Error): Promise<void>;
|
|
19
|
+
hasServeHandler(): Promise<boolean>;
|
|
20
|
+
hasActiveConnections(): Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
export declare class IsolateServer {
|
|
23
|
+
private readonly namespaceId;
|
|
24
|
+
private readonly getConnection;
|
|
25
|
+
private runtime;
|
|
26
|
+
private lastStartOptions;
|
|
27
|
+
private lifecycleLock;
|
|
28
|
+
private closed;
|
|
29
|
+
readonly fetch: IsolateServerFetch;
|
|
30
|
+
constructor(options: IsolateServerOptions);
|
|
31
|
+
start(options: IsolateServerStartOptions): Promise<void>;
|
|
32
|
+
reload(): Promise<void>;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
getRuntime(): RemoteRuntime | null;
|
|
35
|
+
private withLifecycleLock;
|
|
36
|
+
private buildRuntimeOptions;
|
|
37
|
+
private createAndInitializeRuntime;
|
|
38
|
+
private disposeRuntime;
|
|
39
|
+
private ensureStarted;
|
|
40
|
+
private getActiveRuntime;
|
|
41
|
+
private dispatchRequestWithRetry;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/isolate-server",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"main": "./dist/cjs/index.cjs",
|
|
5
|
+
"types": "./dist/types/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"require": "./dist/cjs/index.cjs",
|
|
10
|
+
"import": "./dist/mjs/index.mjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "node --test --experimental-strip-types 'src/**/*.test.ts'",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@ricsam/isolate-client": "*"
|
|
20
|
+
},
|
|
21
|
+
"author": "ricsam <oss@ricsam.dev>",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/ricsam/isolate.git",
|
|
26
|
+
"directory": "packages/isolate-server"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/ricsam/isolate/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/ricsam/isolate#readme",
|
|
5
32
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
33
|
+
"isolated-vm",
|
|
34
|
+
"sandbox",
|
|
35
|
+
"javascript",
|
|
36
|
+
"runtime",
|
|
37
|
+
"fetch",
|
|
38
|
+
"filesystem",
|
|
39
|
+
"streams",
|
|
40
|
+
"v8",
|
|
41
|
+
"isolate"
|
|
42
|
+
],
|
|
43
|
+
"description": "Reusable lifecycle manager for namespaced isolate runtimes and entry-module startup",
|
|
44
|
+
"module": "./dist/mjs/index.mjs",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md"
|
|
9
51
|
]
|
|
10
|
-
}
|
|
52
|
+
}
|