@b9g/platform-node 0.1.14-beta.0 → 0.1.15
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 +13 -5
- package/src/index.d.ts +10 -41
- package/src/index.js +50 -111
- package/src/platform.d.ts +40 -0
- package/src/platform.js +146 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/platform-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Node.js platform adapter for Shovel with hot reloading and ESBuild integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shovel",
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"esbuild"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@b9g/cache": "^0.2.0
|
|
15
|
-
"@b9g/http-errors": "^0.2.0
|
|
16
|
-
"@b9g/node-webworker": "^0.2.0
|
|
17
|
-
"@b9g/platform": "^0.1.
|
|
14
|
+
"@b9g/cache": "^0.2.0",
|
|
15
|
+
"@b9g/http-errors": "^0.2.0",
|
|
16
|
+
"@b9g/node-webworker": "^0.2.0",
|
|
17
|
+
"@b9g/platform": "^0.1.15",
|
|
18
18
|
"@logtape/logtape": "^1.2.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
"./index.js": {
|
|
38
38
|
"types": "./src/index.d.ts",
|
|
39
39
|
"import": "./src/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./platform": {
|
|
42
|
+
"types": "./src/platform.d.ts",
|
|
43
|
+
"import": "./src/platform.js"
|
|
44
|
+
},
|
|
45
|
+
"./platform.js": {
|
|
46
|
+
"types": "./src/platform.d.ts",
|
|
47
|
+
"import": "./src/platform.js"
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides hot reloading, ESBuild integration, and optimized caching for Node.js environments.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
8
|
-
import { BasePlatform, type PlatformConfig, type PlatformDefaults, type Handler, type Server, type ServerOptions, type PlatformESBuildConfig, type ProductionEntryPoints, ServiceWorkerPool, CustomLoggerStorage, CustomDatabaseStorage } from "@b9g/platform";
|
|
6
|
+
import { type PlatformDefaults, type Handler, type Server, type ServerOptions, type PlatformESBuildConfig, type EntryPoints, ServiceWorkerPool } from "@b9g/platform";
|
|
9
7
|
import { type ShovelConfig } from "@b9g/platform/runtime";
|
|
10
|
-
export interface NodePlatformOptions
|
|
8
|
+
export interface NodePlatformOptions {
|
|
11
9
|
/** Port for development server (default: 3000) */
|
|
12
10
|
port?: number;
|
|
13
11
|
/** Host for development server (default: localhost) */
|
|
@@ -68,7 +66,7 @@ export declare class NodeServiceWorkerContainer extends EventTarget implements S
|
|
|
68
66
|
* Node.js platform implementation
|
|
69
67
|
* ServiceWorker entrypoint loader for Node.js with ESBuild VM system
|
|
70
68
|
*/
|
|
71
|
-
export declare class NodePlatform
|
|
69
|
+
export declare class NodePlatform {
|
|
72
70
|
#private;
|
|
73
71
|
readonly name: string;
|
|
74
72
|
readonly serviceWorker: NodeServiceWorkerContainer;
|
|
@@ -97,39 +95,7 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
97
95
|
config?: ShovelConfig;
|
|
98
96
|
};
|
|
99
97
|
/**
|
|
100
|
-
* Create
|
|
101
|
-
*
|
|
102
|
-
* Default: MemoryCache (in-process LRU cache).
|
|
103
|
-
* Override via shovel.json caches config.
|
|
104
|
-
* Note: Used for dev/testing - production uses generated config module.
|
|
105
|
-
*/
|
|
106
|
-
createCaches(): Promise<CustomCacheStorage>;
|
|
107
|
-
/**
|
|
108
|
-
* Create directory storage for Node.js
|
|
109
|
-
*
|
|
110
|
-
* Defaults:
|
|
111
|
-
* - server: NodeFSDirectory at cwd (app files)
|
|
112
|
-
* - public: NodeFSDirectory at cwd (static assets)
|
|
113
|
-
* - tmp: NodeFSDirectory at OS temp dir
|
|
114
|
-
*
|
|
115
|
-
* Override via shovel.json directories config.
|
|
116
|
-
*/
|
|
117
|
-
createDirectories(): Promise<CustomDirectoryStorage>;
|
|
118
|
-
/**
|
|
119
|
-
* Create logger storage for Node.js
|
|
120
|
-
*
|
|
121
|
-
* Uses LogTape for structured logging.
|
|
122
|
-
*/
|
|
123
|
-
createLoggers(): Promise<CustomLoggerStorage>;
|
|
124
|
-
/**
|
|
125
|
-
* Create database storage for Node.js
|
|
126
|
-
*
|
|
127
|
-
* Returns undefined if no databases configured in shovel.json.
|
|
128
|
-
* Supports SQLite via better-sqlite3.
|
|
129
|
-
*/
|
|
130
|
-
createDatabases(configOverride?: NodePlatformOptions["config"]): CustomDatabaseStorage | undefined;
|
|
131
|
-
/**
|
|
132
|
-
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
98
|
+
* Create HTTP server for Node.js
|
|
133
99
|
*/
|
|
134
100
|
createServer(handler: Handler, options?: ServerOptions): Server;
|
|
135
101
|
/**
|
|
@@ -139,13 +105,16 @@ export declare class NodePlatform extends BasePlatform {
|
|
|
139
105
|
*/
|
|
140
106
|
reloadWorkers(entrypoint: string): Promise<void>;
|
|
141
107
|
/**
|
|
142
|
-
* Get
|
|
108
|
+
* Get entry points for bundling.
|
|
109
|
+
*
|
|
110
|
+
* Development mode:
|
|
111
|
+
* - worker.js: Single worker with message loop (develop command acts as supervisor)
|
|
143
112
|
*
|
|
144
|
-
*
|
|
113
|
+
* Production mode:
|
|
145
114
|
* - index.js: Supervisor that spawns workers and owns the HTTP server
|
|
146
115
|
* - worker.js: Worker that handles requests via message loop
|
|
147
116
|
*/
|
|
148
|
-
|
|
117
|
+
getEntryPoints(userEntryPath: string, mode: "development" | "production"): EntryPoints;
|
|
149
118
|
/**
|
|
150
119
|
* Get Node.js-specific esbuild configuration
|
|
151
120
|
*
|
package/src/index.js
CHANGED
|
@@ -6,25 +6,16 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import * as Path from "node:path";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
9
|
-
import { MemoryCache } from "@b9g/cache/memory";
|
|
10
|
-
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
11
|
-
import { NodeFSDirectory } from "@b9g/filesystem/node-fs";
|
|
12
9
|
import { InternalServerError, isHTTPError } from "@b9g/http-errors";
|
|
13
10
|
import {
|
|
14
|
-
|
|
15
|
-
ServiceWorkerPool,
|
|
16
|
-
CustomLoggerStorage,
|
|
17
|
-
CustomDatabaseStorage,
|
|
18
|
-
createDatabaseFactory,
|
|
19
|
-
mergeConfigWithDefaults
|
|
11
|
+
ServiceWorkerPool
|
|
20
12
|
} from "@b9g/platform";
|
|
21
13
|
import {
|
|
22
14
|
ShovelServiceWorkerRegistration,
|
|
23
15
|
kServiceWorker,
|
|
24
|
-
createCacheFactory
|
|
25
|
-
createDirectoryFactory
|
|
16
|
+
createCacheFactory
|
|
26
17
|
} from "@b9g/platform/runtime";
|
|
27
|
-
import { MemoryCache
|
|
18
|
+
import { MemoryCache } from "@b9g/cache/memory";
|
|
28
19
|
var logger = getLogger(["shovel", "platform"]);
|
|
29
20
|
var NodeServiceWorkerContainer = class extends EventTarget {
|
|
30
21
|
#platform;
|
|
@@ -140,14 +131,12 @@ var NodeServiceWorkerContainer = class extends EventTarget {
|
|
|
140
131
|
await this.#pool?.reloadWorkers(entrypoint);
|
|
141
132
|
}
|
|
142
133
|
};
|
|
143
|
-
var NodePlatform = class
|
|
134
|
+
var NodePlatform = class {
|
|
144
135
|
name;
|
|
145
136
|
serviceWorker;
|
|
146
137
|
#options;
|
|
147
|
-
#databaseStorage;
|
|
148
138
|
#server;
|
|
149
139
|
constructor(options = {}) {
|
|
150
|
-
super(options);
|
|
151
140
|
this.name = "node";
|
|
152
141
|
const cwd = options.cwd || process.cwd();
|
|
153
142
|
this.#options = {
|
|
@@ -195,66 +184,7 @@ var NodePlatform = class extends BasePlatform {
|
|
|
195
184
|
return this.#options;
|
|
196
185
|
}
|
|
197
186
|
/**
|
|
198
|
-
* Create
|
|
199
|
-
*
|
|
200
|
-
* Default: MemoryCache (in-process LRU cache).
|
|
201
|
-
* Override via shovel.json caches config.
|
|
202
|
-
* Note: Used for dev/testing - production uses generated config module.
|
|
203
|
-
*/
|
|
204
|
-
async createCaches() {
|
|
205
|
-
const defaults = { default: { impl: MemoryCache } };
|
|
206
|
-
const configs = mergeConfigWithDefaults(
|
|
207
|
-
defaults,
|
|
208
|
-
this.#options.config?.caches
|
|
209
|
-
);
|
|
210
|
-
return new CustomCacheStorage(createCacheFactory({ configs }));
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Create directory storage for Node.js
|
|
214
|
-
*
|
|
215
|
-
* Defaults:
|
|
216
|
-
* - server: NodeFSDirectory at cwd (app files)
|
|
217
|
-
* - public: NodeFSDirectory at cwd (static assets)
|
|
218
|
-
* - tmp: NodeFSDirectory at OS temp dir
|
|
219
|
-
*
|
|
220
|
-
* Override via shovel.json directories config.
|
|
221
|
-
*/
|
|
222
|
-
async createDirectories() {
|
|
223
|
-
const defaults = {
|
|
224
|
-
server: { impl: NodeFSDirectory, path: this.#options.cwd },
|
|
225
|
-
public: { impl: NodeFSDirectory, path: this.#options.cwd },
|
|
226
|
-
tmp: { impl: NodeFSDirectory, path: tmpdir() }
|
|
227
|
-
};
|
|
228
|
-
const configs = mergeConfigWithDefaults(
|
|
229
|
-
defaults,
|
|
230
|
-
this.#options.config?.directories
|
|
231
|
-
);
|
|
232
|
-
return new CustomDirectoryStorage(createDirectoryFactory(configs));
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Create logger storage for Node.js
|
|
236
|
-
*
|
|
237
|
-
* Uses LogTape for structured logging.
|
|
238
|
-
*/
|
|
239
|
-
async createLoggers() {
|
|
240
|
-
return new CustomLoggerStorage((categories) => getLogger(categories));
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Create database storage for Node.js
|
|
244
|
-
*
|
|
245
|
-
* Returns undefined if no databases configured in shovel.json.
|
|
246
|
-
* Supports SQLite via better-sqlite3.
|
|
247
|
-
*/
|
|
248
|
-
createDatabases(configOverride) {
|
|
249
|
-
const config = configOverride ?? this.#options.config;
|
|
250
|
-
if (config?.databases && Object.keys(config.databases).length > 0) {
|
|
251
|
-
const factory = createDatabaseFactory(config.databases);
|
|
252
|
-
return new CustomDatabaseStorage(factory);
|
|
253
|
-
}
|
|
254
|
-
return void 0;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
187
|
+
* Create HTTP server for Node.js
|
|
258
188
|
*/
|
|
259
189
|
createServer(handler, options = {}) {
|
|
260
190
|
const port = options.port ?? this.#options.port;
|
|
@@ -291,8 +221,15 @@ var NodePlatform = class extends BasePlatform {
|
|
|
291
221
|
}
|
|
292
222
|
} catch (error) {
|
|
293
223
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
294
|
-
logger.error("Request error: {error}", { error: err });
|
|
295
224
|
const httpError = isHTTPError(error) ? error : new InternalServerError(err.message, { cause: err });
|
|
225
|
+
if (httpError.status >= 500) {
|
|
226
|
+
logger.error("Request error: {error}", { error: err });
|
|
227
|
+
} else {
|
|
228
|
+
logger.warn("Request error: {status} {error}", {
|
|
229
|
+
status: httpError.status,
|
|
230
|
+
error: err
|
|
231
|
+
});
|
|
232
|
+
}
|
|
296
233
|
const isDev = import.meta.env?.MODE !== "production";
|
|
297
234
|
const response = httpError.toResponse(isDev);
|
|
298
235
|
res.statusCode = response.status;
|
|
@@ -351,13 +288,45 @@ var NodePlatform = class extends BasePlatform {
|
|
|
351
288
|
await this.serviceWorker.reloadWorkers(entrypoint);
|
|
352
289
|
}
|
|
353
290
|
/**
|
|
354
|
-
* Get
|
|
291
|
+
* Get entry points for bundling.
|
|
292
|
+
*
|
|
293
|
+
* Development mode:
|
|
294
|
+
* - worker.js: Single worker with message loop (develop command acts as supervisor)
|
|
355
295
|
*
|
|
356
|
-
*
|
|
296
|
+
* Production mode:
|
|
357
297
|
* - index.js: Supervisor that spawns workers and owns the HTTP server
|
|
358
298
|
* - worker.js: Worker that handles requests via message loop
|
|
359
299
|
*/
|
|
360
|
-
|
|
300
|
+
getEntryPoints(userEntryPath, mode) {
|
|
301
|
+
const workerCode = `// Node.js Worker
|
|
302
|
+
import {parentPort} from "node:worker_threads";
|
|
303
|
+
import {configureLogging, initWorkerRuntime, runLifecycle, startWorkerMessageLoop} from "@b9g/platform/runtime";
|
|
304
|
+
import {config} from "shovel:config";
|
|
305
|
+
|
|
306
|
+
await configureLogging(config.logging);
|
|
307
|
+
|
|
308
|
+
// Initialize worker runtime (installs ServiceWorker globals)
|
|
309
|
+
const {registration, databases} = await initWorkerRuntime({config});
|
|
310
|
+
|
|
311
|
+
// Import user code (registers event handlers)
|
|
312
|
+
await import("${userEntryPath}");
|
|
313
|
+
|
|
314
|
+
// Run ServiceWorker lifecycle (stage from config.lifecycle if present)
|
|
315
|
+
await runLifecycle(registration, config.lifecycle?.stage);
|
|
316
|
+
|
|
317
|
+
// Start message loop for request handling, or signal ready and exit in lifecycle-only mode
|
|
318
|
+
if (config.lifecycle) {
|
|
319
|
+
parentPort?.postMessage({type: "ready"});
|
|
320
|
+
// Clean shutdown after lifecycle
|
|
321
|
+
if (databases) await databases.closeAll();
|
|
322
|
+
process.exit(0);
|
|
323
|
+
} else {
|
|
324
|
+
startWorkerMessageLoop({registration, databases});
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
327
|
+
if (mode === "development") {
|
|
328
|
+
return { worker: workerCode };
|
|
329
|
+
}
|
|
361
330
|
const supervisorCode = `// Node.js Production Supervisor
|
|
362
331
|
import {Worker} from "@b9g/node-webworker";
|
|
363
332
|
import {getLogger} from "@logtape/logtape";
|
|
@@ -390,35 +359,9 @@ const handleShutdown = async () => {
|
|
|
390
359
|
};
|
|
391
360
|
process.on("SIGINT", handleShutdown);
|
|
392
361
|
process.on("SIGTERM", handleShutdown);
|
|
393
|
-
`;
|
|
394
|
-
const workerCode = `// Node.js Production Worker
|
|
395
|
-
import {parentPort} from "node:worker_threads";
|
|
396
|
-
import {configureLogging, initWorkerRuntime, runLifecycle, startWorkerMessageLoop} from "@b9g/platform/runtime";
|
|
397
|
-
import {config} from "shovel:config";
|
|
398
|
-
|
|
399
|
-
await configureLogging(config.logging);
|
|
400
|
-
|
|
401
|
-
// Initialize worker runtime (installs ServiceWorker globals)
|
|
402
|
-
const {registration, databases} = await initWorkerRuntime({config});
|
|
403
|
-
|
|
404
|
-
// Import user code (registers event handlers)
|
|
405
|
-
await import("${userEntryPath}");
|
|
406
|
-
|
|
407
|
-
// Run ServiceWorker lifecycle (stage from config.lifecycle if present)
|
|
408
|
-
await runLifecycle(registration, config.lifecycle?.stage);
|
|
409
|
-
|
|
410
|
-
// Start message loop for request handling, or signal ready and exit in lifecycle-only mode
|
|
411
|
-
if (config.lifecycle) {
|
|
412
|
-
parentPort?.postMessage({type: "ready"});
|
|
413
|
-
// Clean shutdown after lifecycle
|
|
414
|
-
if (databases) await databases.closeAll();
|
|
415
|
-
process.exit(0);
|
|
416
|
-
} else {
|
|
417
|
-
startWorkerMessageLoop({registration, databases});
|
|
418
|
-
}
|
|
419
362
|
`;
|
|
420
363
|
return {
|
|
421
|
-
|
|
364
|
+
supervisor: supervisorCode,
|
|
422
365
|
worker: workerCode
|
|
423
366
|
};
|
|
424
367
|
}
|
|
@@ -477,10 +420,6 @@ if (config.lifecycle) {
|
|
|
477
420
|
async dispose() {
|
|
478
421
|
await this.close();
|
|
479
422
|
await this.serviceWorker.terminate();
|
|
480
|
-
if (this.#databaseStorage) {
|
|
481
|
-
await this.#databaseStorage.closeAll();
|
|
482
|
-
this.#databaseStorage = void 0;
|
|
483
|
-
}
|
|
484
423
|
}
|
|
485
424
|
// =========================================================================
|
|
486
425
|
// Config Expression Method Overrides
|
|
@@ -494,7 +433,7 @@ if (config.lifecycle) {
|
|
|
494
433
|
};
|
|
495
434
|
var src_default = NodePlatform;
|
|
496
435
|
export {
|
|
497
|
-
|
|
436
|
+
MemoryCache as DefaultCache,
|
|
498
437
|
NodePlatform,
|
|
499
438
|
NodeServiceWorkerContainer,
|
|
500
439
|
src_default as default
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Platform Module
|
|
3
|
+
*
|
|
4
|
+
* Build-time and dev-time functions for Node.js.
|
|
5
|
+
* Runtime functions are in ./runtime.ts
|
|
6
|
+
*/
|
|
7
|
+
import type { EntryPoints, ESBuildConfig, PlatformDefaults, DevServerOptions, DevServer } from "@b9g/platform/module";
|
|
8
|
+
export declare const name = "node";
|
|
9
|
+
/**
|
|
10
|
+
* Get entry points for bundling.
|
|
11
|
+
*
|
|
12
|
+
* Development mode:
|
|
13
|
+
* - worker.js: Single worker with message loop (develop command acts as supervisor)
|
|
14
|
+
*
|
|
15
|
+
* Production mode:
|
|
16
|
+
* - supervisor.js: Spawns workers and owns the HTTP server
|
|
17
|
+
* - worker.js: Handles requests via message loop
|
|
18
|
+
*/
|
|
19
|
+
export declare function getEntryPoints(userEntryPath: string, mode: "development" | "production"): EntryPoints;
|
|
20
|
+
/**
|
|
21
|
+
* Get ESBuild configuration for Node.js.
|
|
22
|
+
*
|
|
23
|
+
* Note: Node.js doesn't support import.meta.env natively, so we alias it
|
|
24
|
+
* to process.env for compatibility with code that uses Vite-style env access.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getESBuildConfig(): ESBuildConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Get platform defaults for config generation.
|
|
29
|
+
*
|
|
30
|
+
* Provides default directories (server, public, tmp) that work
|
|
31
|
+
* out of the box for Node.js deployments.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getDefaults(): PlatformDefaults;
|
|
34
|
+
/**
|
|
35
|
+
* Create a dev server using ServiceWorkerPool.
|
|
36
|
+
*
|
|
37
|
+
* Dynamically imports the platform class to keep heavy dependencies
|
|
38
|
+
* out of production bundles.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createDevServer(options: DevServerOptions): Promise<DevServer>;
|
package/src/platform.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/// <reference types="./platform.d.ts" />
|
|
2
|
+
// src/platform.ts
|
|
3
|
+
import { builtinModules } from "node:module";
|
|
4
|
+
import { getLogger } from "@logtape/logtape";
|
|
5
|
+
var logger = getLogger(["shovel", "platform"]);
|
|
6
|
+
var name = "node";
|
|
7
|
+
function getEntryPoints(userEntryPath, mode) {
|
|
8
|
+
const safePath = JSON.stringify(userEntryPath);
|
|
9
|
+
const workerCode = `// Node.js Worker
|
|
10
|
+
import {parentPort} from "node:worker_threads";
|
|
11
|
+
import {configureLogging, initWorkerRuntime, runLifecycle, startWorkerMessageLoop} from "@b9g/platform/runtime";
|
|
12
|
+
import {config} from "shovel:config";
|
|
13
|
+
|
|
14
|
+
await configureLogging(config.logging);
|
|
15
|
+
|
|
16
|
+
// Initialize worker runtime (installs ServiceWorker globals)
|
|
17
|
+
const {registration, databases} = await initWorkerRuntime({config});
|
|
18
|
+
|
|
19
|
+
// Import user code (registers event handlers)
|
|
20
|
+
await import(${safePath});
|
|
21
|
+
|
|
22
|
+
// Run ServiceWorker lifecycle (stage from config.lifecycle if present)
|
|
23
|
+
await runLifecycle(registration, config.lifecycle?.stage);
|
|
24
|
+
|
|
25
|
+
// Start message loop for request handling, or signal ready and exit in lifecycle-only mode
|
|
26
|
+
if (config.lifecycle) {
|
|
27
|
+
parentPort?.postMessage({type: "ready"});
|
|
28
|
+
// Clean shutdown after lifecycle
|
|
29
|
+
if (databases) await databases.closeAll();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
} else {
|
|
32
|
+
startWorkerMessageLoop({registration, databases});
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
if (mode === "development") {
|
|
36
|
+
return { worker: workerCode };
|
|
37
|
+
}
|
|
38
|
+
const supervisorCode = `// Node.js Production Supervisor
|
|
39
|
+
import {Worker} from "@b9g/node-webworker";
|
|
40
|
+
import {getLogger} from "@logtape/logtape";
|
|
41
|
+
import {configureLogging} from "@b9g/platform/runtime";
|
|
42
|
+
import NodePlatform from "@b9g/platform-node";
|
|
43
|
+
import {config} from "shovel:config";
|
|
44
|
+
|
|
45
|
+
await configureLogging(config.logging);
|
|
46
|
+
const logger = getLogger(["shovel", "platform"]);
|
|
47
|
+
|
|
48
|
+
logger.info("Starting production server", {port: config.port, workers: config.workers});
|
|
49
|
+
|
|
50
|
+
// Initialize platform and register ServiceWorker
|
|
51
|
+
// Override createWorker to use the imported Worker class (avoids require() issues with ESM)
|
|
52
|
+
const platform = new NodePlatform({port: config.port, host: config.host, workers: config.workers});
|
|
53
|
+
platform.createWorker = (entrypoint) => new Worker(entrypoint);
|
|
54
|
+
await platform.serviceWorker.register(new URL("./worker.js", import.meta.url).href);
|
|
55
|
+
await platform.serviceWorker.ready;
|
|
56
|
+
|
|
57
|
+
// Start HTTP server
|
|
58
|
+
await platform.listen();
|
|
59
|
+
|
|
60
|
+
logger.info("Server started", {port: config.port, host: config.host, workers: config.workers});
|
|
61
|
+
|
|
62
|
+
// Graceful shutdown
|
|
63
|
+
const handleShutdown = async () => {
|
|
64
|
+
logger.info("Shutting down");
|
|
65
|
+
await platform.close();
|
|
66
|
+
process.exit(0);
|
|
67
|
+
};
|
|
68
|
+
process.on("SIGINT", handleShutdown);
|
|
69
|
+
process.on("SIGTERM", handleShutdown);
|
|
70
|
+
`;
|
|
71
|
+
return {
|
|
72
|
+
supervisor: supervisorCode,
|
|
73
|
+
worker: workerCode
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function getESBuildConfig() {
|
|
77
|
+
return {
|
|
78
|
+
platform: "node",
|
|
79
|
+
external: ["node:*", ...builtinModules],
|
|
80
|
+
define: {
|
|
81
|
+
// Node.js doesn't support import.meta.env, alias to process.env
|
|
82
|
+
"import.meta.env": "process.env"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function getDefaults() {
|
|
87
|
+
return {
|
|
88
|
+
caches: {
|
|
89
|
+
default: {
|
|
90
|
+
module: "@b9g/cache/memory",
|
|
91
|
+
export: "MemoryCache"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
directories: {
|
|
95
|
+
server: {
|
|
96
|
+
module: "@b9g/filesystem/node-fs",
|
|
97
|
+
export: "NodeFSDirectory",
|
|
98
|
+
path: "[outdir]/server"
|
|
99
|
+
},
|
|
100
|
+
public: {
|
|
101
|
+
module: "@b9g/filesystem/node-fs",
|
|
102
|
+
export: "NodeFSDirectory",
|
|
103
|
+
path: "[outdir]/public"
|
|
104
|
+
},
|
|
105
|
+
tmp: {
|
|
106
|
+
module: "@b9g/filesystem/node-fs",
|
|
107
|
+
export: "NodeFSDirectory",
|
|
108
|
+
path: "[tmpdir]"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async function createDevServer(options) {
|
|
114
|
+
const { port, host, workerPath, workers = 1 } = options;
|
|
115
|
+
logger.info("Starting Node.js dev server", { workerPath, workers });
|
|
116
|
+
const { default: NodePlatform } = await import("./index.js");
|
|
117
|
+
const platform = new NodePlatform({
|
|
118
|
+
port,
|
|
119
|
+
host,
|
|
120
|
+
workers
|
|
121
|
+
});
|
|
122
|
+
await platform.serviceWorker.register(workerPath);
|
|
123
|
+
await platform.serviceWorker.ready;
|
|
124
|
+
await platform.listen();
|
|
125
|
+
logger.info("Node.js dev server ready");
|
|
126
|
+
const url = `http://${host}:${port}`;
|
|
127
|
+
return {
|
|
128
|
+
url,
|
|
129
|
+
async reload(newWorkerPath) {
|
|
130
|
+
logger.info("Reloading workers", { workerPath: newWorkerPath });
|
|
131
|
+
await platform.serviceWorker.reloadWorkers(newWorkerPath);
|
|
132
|
+
logger.info("Workers reloaded");
|
|
133
|
+
},
|
|
134
|
+
async close() {
|
|
135
|
+
logger.info("Stopping Node.js dev server");
|
|
136
|
+
await platform.dispose();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
createDevServer,
|
|
142
|
+
getDefaults,
|
|
143
|
+
getESBuildConfig,
|
|
144
|
+
getEntryPoints,
|
|
145
|
+
name
|
|
146
|
+
};
|