@b9g/platform-node 0.1.2 → 0.1.4
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/package.json +3 -3
- package/src/platform.d.ts +6 -2
- package/src/platform.js +66 -161
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/platform-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Node.js platform adapter for Shovel with hot reloading and ESBuild integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shovel",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"esbuild"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@b9g/platform": "
|
|
15
|
-
"@b9g/cache": "
|
|
14
|
+
"@b9g/platform": "workspace:*",
|
|
15
|
+
"@b9g/cache": "workspace:*",
|
|
16
16
|
"@remix-run/node-fetch-server": "^0.11.0",
|
|
17
17
|
"@aws-sdk/client-s3": "^3.0.0"
|
|
18
18
|
},
|
package/src/platform.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface NodePlatformOptions extends PlatformConfig {
|
|
|
23
23
|
export declare class NodePlatform extends BasePlatform {
|
|
24
24
|
readonly name = "node";
|
|
25
25
|
private options;
|
|
26
|
-
private
|
|
26
|
+
private workerPool?;
|
|
27
27
|
private cacheStorage?;
|
|
28
28
|
constructor(options?: NodePlatformOptions);
|
|
29
29
|
/**
|
|
@@ -41,7 +41,7 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
41
41
|
protected getDefaultCacheConfig(): CacheConfig;
|
|
42
42
|
/**
|
|
43
43
|
* SUPPORTING UTILITY - Create cache storage optimized for Node.js
|
|
44
|
-
*
|
|
44
|
+
* Uses MemoryCache in main thread, PostMessageCache in workers
|
|
45
45
|
*/
|
|
46
46
|
createCaches(config?: CacheConfig): Promise<CustomCacheStorage>;
|
|
47
47
|
/**
|
|
@@ -52,6 +52,10 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
52
52
|
* Get filesystem root for File System Access API
|
|
53
53
|
*/
|
|
54
54
|
getFileSystemRoot(name?: string): Promise<FileSystemDirectoryHandle>;
|
|
55
|
+
/**
|
|
56
|
+
* Reload workers for hot reloading (called by CLI)
|
|
57
|
+
*/
|
|
58
|
+
reloadWorkers(version?: number | string): Promise<void>;
|
|
55
59
|
/**
|
|
56
60
|
* Dispose of platform resources
|
|
57
61
|
*/
|
package/src/platform.js
CHANGED
|
@@ -3,157 +3,41 @@
|
|
|
3
3
|
import {
|
|
4
4
|
BasePlatform
|
|
5
5
|
} from "@b9g/platform";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { WorkerPool } from "@b9g/platform/worker-pool";
|
|
7
|
+
import { CustomCacheStorage, MemoryCache, MemoryCacheManager, PostMessageCache } from "@b9g/cache";
|
|
8
|
+
import { FileSystemRegistry, getDirectoryHandle, LocalBucket } from "@b9g/filesystem";
|
|
8
9
|
import * as Http from "http";
|
|
9
10
|
import * as Path from "path";
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from "url";
|
|
12
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
var __dirname = Path.dirname(__filename);
|
|
14
|
-
var WorkerManager = class {
|
|
15
|
-
constructor(cacheStorage, options, workerCount = 1, entrypoint) {
|
|
16
|
-
this.entrypoint = entrypoint;
|
|
17
|
-
this.memoryCacheManager = new MemoryCacheManager();
|
|
18
|
-
this.options = options;
|
|
19
|
-
console.info(
|
|
20
|
-
"[WorkerManager] Constructor called with entrypoint:",
|
|
21
|
-
entrypoint
|
|
22
|
-
);
|
|
23
|
-
this.initWorkers(workerCount);
|
|
24
|
-
}
|
|
25
|
-
workers = [];
|
|
26
|
-
currentWorker = 0;
|
|
27
|
-
requestId = 0;
|
|
28
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
11
|
+
var NodeWorkerPool = class extends WorkerPool {
|
|
29
12
|
memoryCacheManager;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.createWorker();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
createWorker() {
|
|
37
|
-
let workerScript;
|
|
38
|
-
try {
|
|
39
|
-
const workerUrl = import.meta.resolve("@b9g/shovel/worker.js");
|
|
40
|
-
workerScript = fileURLToPath(workerUrl);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
`Could not resolve @b9g/shovel/worker.js: ${error.message}`
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
const worker = new Worker(workerScript);
|
|
47
|
-
worker.on("message", (message) => {
|
|
48
|
-
if (message.type?.startsWith("cache:")) {
|
|
49
|
-
this.memoryCacheManager.handleMessage(worker, message);
|
|
50
|
-
} else {
|
|
51
|
-
this.handleWorkerMessage(message);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
worker.on("error", (error) => {
|
|
55
|
-
console.error("[Platform-Node] Worker error:", error);
|
|
56
|
-
});
|
|
57
|
-
this.workers.push(worker);
|
|
58
|
-
return worker;
|
|
59
|
-
}
|
|
60
|
-
handleWorkerMessage(message) {
|
|
61
|
-
if (message.type === "response" && message.requestId) {
|
|
62
|
-
const pending = this.pendingRequests.get(message.requestId);
|
|
63
|
-
if (pending) {
|
|
64
|
-
const response = new Response(message.response.body, {
|
|
65
|
-
status: message.response.status,
|
|
66
|
-
statusText: message.response.statusText,
|
|
67
|
-
headers: message.response.headers
|
|
68
|
-
});
|
|
69
|
-
pending.resolve(response);
|
|
70
|
-
this.pendingRequests.delete(message.requestId);
|
|
71
|
-
}
|
|
72
|
-
} else if (message.type === "error" && message.requestId) {
|
|
73
|
-
const pending = this.pendingRequests.get(message.requestId);
|
|
74
|
-
if (pending) {
|
|
75
|
-
pending.reject(new Error(message.error));
|
|
76
|
-
this.pendingRequests.delete(message.requestId);
|
|
77
|
-
}
|
|
78
|
-
} else if (message.type === "ready") {
|
|
79
|
-
console.info(`[Platform-Node] ServiceWorker ready (v${message.version})`);
|
|
80
|
-
} else if (message.type === "worker-ready") {
|
|
81
|
-
console.info("[Platform-Node] Worker initialized");
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Handle HTTP request using round-robin Worker selection
|
|
86
|
-
*/
|
|
87
|
-
async handleRequest(request) {
|
|
88
|
-
const worker = this.workers[this.currentWorker];
|
|
13
|
+
constructor(cacheStorage, poolOptions, appEntrypoint) {
|
|
14
|
+
super(cacheStorage, poolOptions, appEntrypoint);
|
|
15
|
+
this.memoryCacheManager = new MemoryCacheManager();
|
|
89
16
|
console.info(
|
|
90
|
-
|
|
17
|
+
"[NodeWorkerPool] Initialized with entrypoint:",
|
|
18
|
+
appEntrypoint
|
|
91
19
|
);
|
|
92
|
-
this.currentWorker = (this.currentWorker + 1) % this.workers.length;
|
|
93
|
-
const requestId = ++this.requestId;
|
|
94
|
-
return new Promise((resolve2, reject) => {
|
|
95
|
-
this.pendingRequests.set(requestId, { resolve: resolve2, reject });
|
|
96
|
-
worker.postMessage({
|
|
97
|
-
type: "request",
|
|
98
|
-
request: {
|
|
99
|
-
url: request.url,
|
|
100
|
-
method: request.method,
|
|
101
|
-
headers: Object.fromEntries(request.headers.entries()),
|
|
102
|
-
body: request.body
|
|
103
|
-
},
|
|
104
|
-
requestId
|
|
105
|
-
});
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
if (this.pendingRequests.has(requestId)) {
|
|
108
|
-
this.pendingRequests.delete(requestId);
|
|
109
|
-
reject(new Error("Request timeout"));
|
|
110
|
-
}
|
|
111
|
-
}, 3e4);
|
|
112
|
-
});
|
|
113
20
|
}
|
|
114
21
|
/**
|
|
115
|
-
*
|
|
22
|
+
* Handle Node.js-specific cache coordination
|
|
116
23
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const handleReady = (message) => {
|
|
122
|
-
if (message.type === "ready" && message.version === version) {
|
|
123
|
-
worker.off("message", handleReady);
|
|
124
|
-
resolve2();
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
console.info("[Platform-Node] Sending load message:", {
|
|
128
|
-
version,
|
|
129
|
-
entrypoint: this.entrypoint
|
|
130
|
-
});
|
|
131
|
-
worker.on("message", handleReady);
|
|
132
|
-
worker.postMessage({
|
|
133
|
-
type: "load",
|
|
134
|
-
version,
|
|
135
|
-
entrypoint: this.entrypoint
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
await Promise.all(loadPromises);
|
|
140
|
-
console.info(`[Platform-Node] All Workers reloaded (v${version})`);
|
|
24
|
+
handleCacheMessage(message) {
|
|
25
|
+
if (message.type?.startsWith("cache:")) {
|
|
26
|
+
console.warn("[NodeWorkerPool] Cache coordination not fully implemented in abstraction");
|
|
27
|
+
}
|
|
141
28
|
}
|
|
142
29
|
/**
|
|
143
|
-
*
|
|
30
|
+
* Enhanced termination with memory cache cleanup
|
|
144
31
|
*/
|
|
145
32
|
async terminate() {
|
|
146
|
-
|
|
147
|
-
await Promise.allSettled(terminatePromises);
|
|
33
|
+
await super.terminate();
|
|
148
34
|
await this.memoryCacheManager.dispose();
|
|
149
|
-
this.workers = [];
|
|
150
|
-
this.pendingRequests.clear();
|
|
151
35
|
}
|
|
152
36
|
};
|
|
153
37
|
var NodePlatform = class extends BasePlatform {
|
|
154
38
|
name = "node";
|
|
155
39
|
options;
|
|
156
|
-
|
|
40
|
+
workerPool;
|
|
157
41
|
cacheStorage;
|
|
158
42
|
constructor(options = {}) {
|
|
159
43
|
super(options);
|
|
@@ -164,7 +48,7 @@ var NodePlatform = class extends BasePlatform {
|
|
|
164
48
|
cwd: process.cwd(),
|
|
165
49
|
...options
|
|
166
50
|
};
|
|
167
|
-
FileSystemRegistry.register("node", new
|
|
51
|
+
FileSystemRegistry.register("node", new LocalBucket({
|
|
168
52
|
rootPath: this.options.cwd
|
|
169
53
|
}));
|
|
170
54
|
}
|
|
@@ -173,7 +57,7 @@ var NodePlatform = class extends BasePlatform {
|
|
|
173
57
|
*/
|
|
174
58
|
async getDirectoryHandle(name) {
|
|
175
59
|
const distPath = Path.resolve(this.options.cwd, "dist");
|
|
176
|
-
const adapter = new
|
|
60
|
+
const adapter = new LocalBucket({ rootPath: distPath });
|
|
177
61
|
return await adapter.getDirectoryHandle(name);
|
|
178
62
|
}
|
|
179
63
|
/**
|
|
@@ -185,29 +69,34 @@ var NodePlatform = class extends BasePlatform {
|
|
|
185
69
|
if (!this.cacheStorage) {
|
|
186
70
|
this.cacheStorage = await this.createCaches(options.caches);
|
|
187
71
|
}
|
|
188
|
-
if (this.
|
|
189
|
-
await this.
|
|
72
|
+
if (this.workerPool) {
|
|
73
|
+
await this.workerPool.terminate();
|
|
190
74
|
}
|
|
191
75
|
const workerCount = options.workerCount || 1;
|
|
192
76
|
console.info(
|
|
193
|
-
"[Platform-Node] Creating
|
|
77
|
+
"[Platform-Node] Creating NodeWorkerPool with entryPath:",
|
|
194
78
|
entryPath
|
|
195
79
|
);
|
|
196
|
-
this.
|
|
80
|
+
this.workerPool = new NodeWorkerPool(
|
|
197
81
|
this.cacheStorage,
|
|
198
|
-
|
|
199
|
-
|
|
82
|
+
{
|
|
83
|
+
workerCount,
|
|
84
|
+
requestTimeout: 3e4,
|
|
85
|
+
hotReload: this.options.hotReload,
|
|
86
|
+
cwd: this.options.cwd
|
|
87
|
+
},
|
|
200
88
|
entryPath
|
|
201
89
|
);
|
|
90
|
+
await this.workerPool.init();
|
|
202
91
|
const version = Date.now();
|
|
203
|
-
await this.
|
|
92
|
+
await this.workerPool.reloadWorkers(version);
|
|
204
93
|
const instance = {
|
|
205
|
-
runtime: this.
|
|
94
|
+
runtime: this.workerPool,
|
|
206
95
|
handleRequest: async (request) => {
|
|
207
|
-
if (!this.
|
|
208
|
-
throw new Error("
|
|
96
|
+
if (!this.workerPool) {
|
|
97
|
+
throw new Error("NodeWorkerPool not initialized");
|
|
209
98
|
}
|
|
210
|
-
return this.
|
|
99
|
+
return this.workerPool.handleRequest(request);
|
|
211
100
|
},
|
|
212
101
|
install: async () => {
|
|
213
102
|
console.info(
|
|
@@ -223,12 +112,12 @@ var NodePlatform = class extends BasePlatform {
|
|
|
223
112
|
return [];
|
|
224
113
|
},
|
|
225
114
|
get ready() {
|
|
226
|
-
return this.
|
|
115
|
+
return this.workerPool?.ready ?? false;
|
|
227
116
|
},
|
|
228
117
|
dispose: async () => {
|
|
229
|
-
if (this.
|
|
230
|
-
await this.
|
|
231
|
-
this.
|
|
118
|
+
if (this.workerPool) {
|
|
119
|
+
await this.workerPool.terminate();
|
|
120
|
+
this.workerPool = void 0;
|
|
232
121
|
}
|
|
233
122
|
console.info("[Platform-Node] ServiceWorker disposed");
|
|
234
123
|
}
|
|
@@ -251,16 +140,24 @@ var NodePlatform = class extends BasePlatform {
|
|
|
251
140
|
}
|
|
252
141
|
/**
|
|
253
142
|
* SUPPORTING UTILITY - Create cache storage optimized for Node.js
|
|
254
|
-
*
|
|
143
|
+
* Uses MemoryCache in main thread, PostMessageCache in workers
|
|
255
144
|
*/
|
|
256
145
|
async createCaches(config) {
|
|
257
|
-
const
|
|
146
|
+
const { isMainThread } = await import("worker_threads");
|
|
258
147
|
return new CustomCacheStorage((name) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
148
|
+
if (isMainThread) {
|
|
149
|
+
return new MemoryCache(name, {
|
|
150
|
+
maxEntries: 1e3,
|
|
151
|
+
maxAge: 60 * 60 * 1e3
|
|
152
|
+
// 1 hour
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
return new PostMessageCache(name, {
|
|
156
|
+
maxEntries: 1e3,
|
|
157
|
+
maxAge: 60 * 60 * 1e3
|
|
158
|
+
// 1 hour
|
|
159
|
+
});
|
|
160
|
+
}
|
|
264
161
|
});
|
|
265
162
|
}
|
|
266
163
|
/**
|
|
@@ -337,15 +234,23 @@ var NodePlatform = class extends BasePlatform {
|
|
|
337
234
|
* Get filesystem root for File System Access API
|
|
338
235
|
*/
|
|
339
236
|
async getFileSystemRoot(name = "default") {
|
|
340
|
-
return await
|
|
237
|
+
return await getDirectoryHandle(name);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Reload workers for hot reloading (called by CLI)
|
|
241
|
+
*/
|
|
242
|
+
async reloadWorkers(version) {
|
|
243
|
+
if (this.workerPool) {
|
|
244
|
+
await this.workerPool.reloadWorkers(version);
|
|
245
|
+
}
|
|
341
246
|
}
|
|
342
247
|
/**
|
|
343
248
|
* Dispose of platform resources
|
|
344
249
|
*/
|
|
345
250
|
async dispose() {
|
|
346
|
-
if (this.
|
|
347
|
-
await this.
|
|
348
|
-
this.
|
|
251
|
+
if (this.workerPool) {
|
|
252
|
+
await this.workerPool.terminate();
|
|
253
|
+
this.workerPool = void 0;
|
|
349
254
|
}
|
|
350
255
|
}
|
|
351
256
|
};
|