@b9g/platform-node 0.1.11 → 0.1.12
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 +2 -2
- package/package.json +3 -3
- package/src/index.d.ts +28 -4
- package/src/index.js +124 -22
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Node.js platform adapter for Shovel. Runs ServiceWorker applications on Node.js
|
|
|
8
8
|
- Hot module reloading for development via VM module system
|
|
9
9
|
- Worker thread pool for concurrent request handling
|
|
10
10
|
- Memory and filesystem cache backends
|
|
11
|
-
- File System Access API implementation via
|
|
11
|
+
- File System Access API implementation via NodeDirectory
|
|
12
12
|
- ServiceWorker lifecycle support (install, activate, fetch events)
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
@@ -112,7 +112,7 @@ Loads and runs a ServiceWorker entrypoint.
|
|
|
112
112
|
Configured via `caches` option:
|
|
113
113
|
|
|
114
114
|
- `memory`: In-memory caching (MemoryCache)
|
|
115
|
-
- `filesystem`: File-based caching (
|
|
115
|
+
- `filesystem`: File-based caching (NodeDirectory)
|
|
116
116
|
|
|
117
117
|
## Worker Thread Architecture
|
|
118
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/platform-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Node.js platform adapter for Shovel with hot reloading and ESBuild integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shovel",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"esbuild"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@b9g/cache": "^0.1.
|
|
14
|
+
"@b9g/cache": "^0.1.5",
|
|
15
15
|
"@b9g/http-errors": "^0.1.5",
|
|
16
|
-
"@b9g/platform": "^0.1.
|
|
16
|
+
"@b9g/platform": "^0.1.12",
|
|
17
17
|
"@logtape/logtape": "^1.2.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
package/src/index.d.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides hot reloading, ESBuild integration, and optimized caching for Node.js environments.
|
|
5
5
|
*/
|
|
6
|
-
import { BasePlatform, PlatformConfig, Handler, Server, ServerOptions, ServiceWorkerOptions, ServiceWorkerInstance, ServiceWorkerPool } from "@b9g/platform";
|
|
6
|
+
import { BasePlatform, type PlatformConfig, type Handler, type Server, type ServerOptions, type ServiceWorkerOptions, type ServiceWorkerInstance, type EntryWrapperOptions, type PlatformEsbuildConfig, ServiceWorkerPool } from "@b9g/platform";
|
|
7
7
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
8
|
-
|
|
8
|
+
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
9
9
|
export interface NodePlatformOptions extends PlatformConfig {
|
|
10
10
|
/** Port for development server (default: 3000) */
|
|
11
11
|
port?: number;
|
|
@@ -13,6 +13,8 @@ export interface NodePlatformOptions extends PlatformConfig {
|
|
|
13
13
|
host?: string;
|
|
14
14
|
/** Working directory for file resolution */
|
|
15
15
|
cwd?: string;
|
|
16
|
+
/** Number of worker threads (default: 1) */
|
|
17
|
+
workers?: number;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Node.js platform implementation
|
|
@@ -37,10 +39,13 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
37
39
|
*/
|
|
38
40
|
loadServiceWorker(entrypoint: string, options?: ServiceWorkerOptions): Promise<ServiceWorkerInstance>;
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
* Uses config from package.json shovel field
|
|
42
|
+
* Create cache storage (in-memory by default)
|
|
42
43
|
*/
|
|
43
44
|
createCaches(): Promise<CustomCacheStorage>;
|
|
45
|
+
/**
|
|
46
|
+
* Create directory storage for the given base directory
|
|
47
|
+
*/
|
|
48
|
+
createDirectories(baseDir: string): CustomDirectoryStorage;
|
|
44
49
|
/**
|
|
45
50
|
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
46
51
|
*/
|
|
@@ -50,6 +55,25 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
50
55
|
* @param entrypoint - Path to the new entrypoint (hashed filename)
|
|
51
56
|
*/
|
|
52
57
|
reloadWorkers(entrypoint: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Get virtual entry wrapper for Node.js
|
|
60
|
+
*
|
|
61
|
+
* Returns production server entry template that uses:
|
|
62
|
+
* - shovel:config virtual module for configuration
|
|
63
|
+
* - Worker threads via ServiceWorkerPool for multi-worker scaling
|
|
64
|
+
* - Platform's loadServiceWorker and createServer methods
|
|
65
|
+
*
|
|
66
|
+
* The template is a real .ts file (entry-template.ts) for better
|
|
67
|
+
* IDE support and linting. It's imported with {type: "text"}.
|
|
68
|
+
*/
|
|
69
|
+
getEntryWrapper(_entryPath: string, _options?: EntryWrapperOptions): string;
|
|
70
|
+
/**
|
|
71
|
+
* Get Node.js-specific esbuild configuration
|
|
72
|
+
*
|
|
73
|
+
* Note: Node.js doesn't support import.meta.env natively, so we alias it
|
|
74
|
+
* to process.env for compatibility with code that uses Vite-style env access.
|
|
75
|
+
*/
|
|
76
|
+
getEsbuildConfig(): PlatformEsbuildConfig;
|
|
53
77
|
/**
|
|
54
78
|
* Dispose of platform resources
|
|
55
79
|
*/
|
package/src/index.js
CHANGED
|
@@ -4,31 +4,87 @@ import {
|
|
|
4
4
|
BasePlatform,
|
|
5
5
|
ServiceWorkerPool,
|
|
6
6
|
SingleThreadedRuntime,
|
|
7
|
-
|
|
8
|
-
createCacheFactory
|
|
7
|
+
CustomLoggerStorage
|
|
9
8
|
} from "@b9g/platform";
|
|
10
9
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
10
|
+
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
11
|
+
import { MemoryCache } from "@b9g/cache/memory";
|
|
12
|
+
import { NodeDirectory } from "@b9g/filesystem/node";
|
|
11
13
|
import { InternalServerError, isHTTPError } from "@b9g/http-errors";
|
|
12
14
|
import * as HTTP from "http";
|
|
13
15
|
import * as Path from "path";
|
|
14
16
|
import { getLogger } from "@logtape/logtape";
|
|
15
|
-
var
|
|
17
|
+
var entryTemplate = `// Node.js Production Server Entry
|
|
18
|
+
// This file is imported as text and used as the entry wrapper template
|
|
19
|
+
import {getLogger} from "@logtape/logtape";
|
|
20
|
+
import {configureLogging} from "@b9g/platform/runtime";
|
|
21
|
+
import {config} from "shovel:config"; // Virtual module - resolved at build time
|
|
22
|
+
import Platform from "@b9g/platform-node";
|
|
23
|
+
|
|
24
|
+
// Configure logging before anything else
|
|
25
|
+
await configureLogging(config.logging);
|
|
26
|
+
|
|
27
|
+
const logger = getLogger(["platform"]);
|
|
28
|
+
|
|
29
|
+
// Configuration from shovel:config (with process.env fallbacks baked in)
|
|
30
|
+
const PORT = config.port;
|
|
31
|
+
const HOST = config.host;
|
|
32
|
+
const WORKER_COUNT = config.workers;
|
|
33
|
+
|
|
34
|
+
logger.info("Starting production server", {});
|
|
35
|
+
logger.info("Workers", {count: WORKER_COUNT});
|
|
36
|
+
|
|
37
|
+
// Create platform instance
|
|
38
|
+
const platform = new Platform();
|
|
39
|
+
|
|
40
|
+
// Get the path to the user's ServiceWorker code
|
|
41
|
+
const userCodeURL = new URL("./server.js", import.meta.url);
|
|
42
|
+
const userCodePath = userCodeURL.pathname;
|
|
43
|
+
|
|
44
|
+
// Load ServiceWorker with worker pool
|
|
45
|
+
const serviceWorker = await platform.loadServiceWorker(userCodePath, {
|
|
46
|
+
workerCount: WORKER_COUNT,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Create HTTP server
|
|
50
|
+
const server = platform.createServer(serviceWorker.handleRequest, {
|
|
51
|
+
port: PORT,
|
|
52
|
+
host: HOST,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await server.listen();
|
|
56
|
+
logger.info("Server running", {url: \`http://\${HOST}:\${PORT}\`});
|
|
57
|
+
logger.info("Load balancing", {workers: WORKER_COUNT});
|
|
58
|
+
|
|
59
|
+
// Graceful shutdown
|
|
60
|
+
const shutdown = async () => {
|
|
61
|
+
logger.info("Shutting down server", {});
|
|
62
|
+
await serviceWorker.dispose();
|
|
63
|
+
await platform.dispose();
|
|
64
|
+
await server.close();
|
|
65
|
+
logger.info("Server stopped", {});
|
|
66
|
+
process.exit(0);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
process.on("SIGINT", shutdown);
|
|
70
|
+
process.on("SIGTERM", shutdown);
|
|
71
|
+
`;
|
|
72
|
+
var logger = getLogger(["platform"]);
|
|
16
73
|
var NodePlatform = class extends BasePlatform {
|
|
17
74
|
name;
|
|
18
75
|
#options;
|
|
19
76
|
#workerPool;
|
|
20
77
|
#singleThreadedRuntime;
|
|
21
78
|
#cacheStorage;
|
|
22
|
-
#
|
|
79
|
+
#directoryStorage;
|
|
23
80
|
constructor(options = {}) {
|
|
24
81
|
super(options);
|
|
25
82
|
this.name = "node";
|
|
26
83
|
const cwd = options.cwd || process.cwd();
|
|
27
|
-
this.#config = loadConfig(cwd);
|
|
28
|
-
logger.info("Loaded configuration", { config: this.#config });
|
|
29
84
|
this.#options = {
|
|
30
|
-
port: options.port ??
|
|
31
|
-
host: options.host ??
|
|
85
|
+
port: options.port ?? 3e3,
|
|
86
|
+
host: options.host ?? "localhost",
|
|
87
|
+
workers: options.workers ?? 1,
|
|
32
88
|
cwd,
|
|
33
89
|
...options
|
|
34
90
|
};
|
|
@@ -53,7 +109,7 @@ var NodePlatform = class extends BasePlatform {
|
|
|
53
109
|
* Uses Worker threads with coordinated cache storage for isolation and standards compliance
|
|
54
110
|
*/
|
|
55
111
|
async loadServiceWorker(entrypoint, options = {}) {
|
|
56
|
-
const workerCount = options.workerCount ?? this.#
|
|
112
|
+
const workerCount = options.workerCount ?? this.#options.workers;
|
|
57
113
|
if (workerCount === 1 && !options.hotReload) {
|
|
58
114
|
return this.#loadServiceWorkerDirect(entrypoint, options);
|
|
59
115
|
}
|
|
@@ -69,6 +125,9 @@ var NodePlatform = class extends BasePlatform {
|
|
|
69
125
|
if (!this.#cacheStorage) {
|
|
70
126
|
this.#cacheStorage = await this.createCaches();
|
|
71
127
|
}
|
|
128
|
+
if (!this.#directoryStorage) {
|
|
129
|
+
this.#directoryStorage = this.createDirectories(entryDir);
|
|
130
|
+
}
|
|
72
131
|
if (this.#singleThreadedRuntime) {
|
|
73
132
|
await this.#singleThreadedRuntime.terminate();
|
|
74
133
|
}
|
|
@@ -78,12 +137,12 @@ var NodePlatform = class extends BasePlatform {
|
|
|
78
137
|
}
|
|
79
138
|
logger.info("Creating single-threaded ServiceWorker runtime", { entryPath });
|
|
80
139
|
this.#singleThreadedRuntime = new SingleThreadedRuntime({
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
140
|
+
caches: this.#cacheStorage,
|
|
141
|
+
directories: this.#directoryStorage,
|
|
142
|
+
loggers: new CustomLoggerStorage((...cats) => getLogger(cats))
|
|
84
143
|
});
|
|
85
144
|
await this.#singleThreadedRuntime.init();
|
|
86
|
-
await this.#singleThreadedRuntime.
|
|
145
|
+
await this.#singleThreadedRuntime.load(entryPath);
|
|
87
146
|
const runtime = this.#singleThreadedRuntime;
|
|
88
147
|
const platform = this;
|
|
89
148
|
const instance = {
|
|
@@ -143,7 +202,8 @@ var NodePlatform = class extends BasePlatform {
|
|
|
143
202
|
},
|
|
144
203
|
entryPath,
|
|
145
204
|
this.#cacheStorage,
|
|
146
|
-
|
|
205
|
+
{}
|
|
206
|
+
// Empty config - use defaults
|
|
147
207
|
);
|
|
148
208
|
await this.#workerPool.init();
|
|
149
209
|
await this.#workerPool.reloadWorkers(entryPath);
|
|
@@ -184,11 +244,26 @@ var NodePlatform = class extends BasePlatform {
|
|
|
184
244
|
return instance;
|
|
185
245
|
}
|
|
186
246
|
/**
|
|
187
|
-
*
|
|
188
|
-
* Uses config from package.json shovel field
|
|
247
|
+
* Create cache storage (in-memory by default)
|
|
189
248
|
*/
|
|
190
249
|
async createCaches() {
|
|
191
|
-
return new CustomCacheStorage(
|
|
250
|
+
return new CustomCacheStorage((name) => new MemoryCache(name));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Create directory storage for the given base directory
|
|
254
|
+
*/
|
|
255
|
+
createDirectories(baseDir) {
|
|
256
|
+
return new CustomDirectoryStorage((name) => {
|
|
257
|
+
let dirPath;
|
|
258
|
+
if (name === "static") {
|
|
259
|
+
dirPath = Path.resolve(baseDir, "../static");
|
|
260
|
+
} else if (name === "server") {
|
|
261
|
+
dirPath = baseDir;
|
|
262
|
+
} else {
|
|
263
|
+
dirPath = Path.resolve(baseDir, `../${name}`);
|
|
264
|
+
}
|
|
265
|
+
return Promise.resolve(new NodeDirectory(dirPath));
|
|
266
|
+
});
|
|
192
267
|
}
|
|
193
268
|
/**
|
|
194
269
|
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
@@ -228,10 +303,7 @@ var NodePlatform = class extends BasePlatform {
|
|
|
228
303
|
}
|
|
229
304
|
} catch (error) {
|
|
230
305
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
231
|
-
logger.error("Request error", {
|
|
232
|
-
error: err.message,
|
|
233
|
-
stack: err.stack
|
|
234
|
-
});
|
|
306
|
+
logger.error("Request error: {error}", { error: err });
|
|
235
307
|
const httpError = isHTTPError(error) ? error : new InternalServerError(err.message, { cause: err });
|
|
236
308
|
const isDev = import.meta.env?.MODE !== "production";
|
|
237
309
|
const response = httpError.toResponse(isDev);
|
|
@@ -290,9 +362,39 @@ var NodePlatform = class extends BasePlatform {
|
|
|
290
362
|
if (this.#workerPool) {
|
|
291
363
|
await this.#workerPool.reloadWorkers(entrypoint);
|
|
292
364
|
} else if (this.#singleThreadedRuntime) {
|
|
293
|
-
await this.#singleThreadedRuntime.
|
|
365
|
+
await this.#singleThreadedRuntime.load(entrypoint);
|
|
294
366
|
}
|
|
295
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Get virtual entry wrapper for Node.js
|
|
370
|
+
*
|
|
371
|
+
* Returns production server entry template that uses:
|
|
372
|
+
* - shovel:config virtual module for configuration
|
|
373
|
+
* - Worker threads via ServiceWorkerPool for multi-worker scaling
|
|
374
|
+
* - Platform's loadServiceWorker and createServer methods
|
|
375
|
+
*
|
|
376
|
+
* The template is a real .ts file (entry-template.ts) for better
|
|
377
|
+
* IDE support and linting. It's imported with {type: "text"}.
|
|
378
|
+
*/
|
|
379
|
+
getEntryWrapper(_entryPath, _options) {
|
|
380
|
+
return entryTemplate;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Get Node.js-specific esbuild configuration
|
|
384
|
+
*
|
|
385
|
+
* Note: Node.js doesn't support import.meta.env natively, so we alias it
|
|
386
|
+
* to process.env for compatibility with code that uses Vite-style env access.
|
|
387
|
+
*/
|
|
388
|
+
getEsbuildConfig() {
|
|
389
|
+
return {
|
|
390
|
+
platform: "node",
|
|
391
|
+
external: ["node:*"],
|
|
392
|
+
define: {
|
|
393
|
+
// Node.js doesn't support import.meta.env, alias to process.env
|
|
394
|
+
"import.meta.env": "process.env"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
296
398
|
/**
|
|
297
399
|
* Dispose of platform resources
|
|
298
400
|
*/
|