@b9g/platform-bun 0.1.9 → 0.1.11
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 +6 -7
- package/src/index.d.ts +65 -5
- package/src/index.js +344 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/platform-bun",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Bun platform adapter for Shovel with hot reloading and built-in TypeScript/JSX support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shovel",
|
|
@@ -12,15 +12,14 @@
|
|
|
12
12
|
"jsx"
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@b9g/assets": "^0.
|
|
16
|
-
"@b9g/cache": "^0.
|
|
17
|
-
"@b9g/http-errors": "^0.
|
|
18
|
-
"@b9g/platform": "^0.1.
|
|
15
|
+
"@b9g/assets": "^0.2.0-beta.0",
|
|
16
|
+
"@b9g/cache": "^0.2.0-beta.0",
|
|
17
|
+
"@b9g/http-errors": "^0.2.0-beta.0",
|
|
18
|
+
"@b9g/platform": "^0.1.13",
|
|
19
19
|
"@logtape/logtape": "^1.2.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@b9g/libuild": "^0.1.18"
|
|
23
|
-
"bun-types": "latest"
|
|
22
|
+
"@b9g/libuild": "^0.1.18"
|
|
24
23
|
},
|
|
25
24
|
"type": "module",
|
|
26
25
|
"types": "src/index.d.ts",
|
package/src/index.d.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides built-in TypeScript/JSX support and simplified server setup for Bun environments.
|
|
5
5
|
*/
|
|
6
|
-
import { BasePlatform, PlatformConfig, Handler, Server, ServerOptions, ServiceWorkerOptions, ServiceWorkerInstance, ServiceWorkerPool } from "@b9g/platform";
|
|
6
|
+
import { BasePlatform, type PlatformConfig, type PlatformDefaults, type Handler, type Server, type ServerOptions, type ServiceWorkerOptions, type ServiceWorkerInstance, type EntryWrapperOptions, type PlatformESBuildConfig, ServiceWorkerPool, CustomLoggerStorage, CustomDatabaseStorage } from "@b9g/platform";
|
|
7
|
+
import { type ShovelConfig } from "@b9g/platform/runtime";
|
|
7
8
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
8
|
-
|
|
9
|
+
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
9
10
|
export interface BunPlatformOptions extends PlatformConfig {
|
|
10
11
|
/** Port for development server (default: 3000) */
|
|
11
12
|
port?: number;
|
|
@@ -13,6 +14,10 @@ export interface BunPlatformOptions extends PlatformConfig {
|
|
|
13
14
|
host?: string;
|
|
14
15
|
/** Working directory for file resolution */
|
|
15
16
|
cwd?: string;
|
|
17
|
+
/** Number of worker threads (default: 1) */
|
|
18
|
+
workers?: number;
|
|
19
|
+
/** Shovel configuration (caches, directories, etc.) */
|
|
20
|
+
config?: ShovelConfig;
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Bun platform implementation
|
|
@@ -25,17 +30,36 @@ export declare class BunPlatform extends BasePlatform {
|
|
|
25
30
|
/**
|
|
26
31
|
* Get options for testing
|
|
27
32
|
*/
|
|
28
|
-
get options():
|
|
33
|
+
get options(): {
|
|
34
|
+
port: number;
|
|
35
|
+
host: string;
|
|
36
|
+
cwd: string;
|
|
37
|
+
workers: number;
|
|
38
|
+
config?: ShovelConfig;
|
|
39
|
+
};
|
|
29
40
|
/**
|
|
30
41
|
* Get/set worker pool for testing
|
|
31
42
|
*/
|
|
32
43
|
get workerPool(): ServiceWorkerPool | undefined;
|
|
33
44
|
set workerPool(pool: ServiceWorkerPool | undefined);
|
|
34
45
|
/**
|
|
35
|
-
* Create cache storage
|
|
36
|
-
*
|
|
46
|
+
* Create cache storage using config from shovel.json
|
|
47
|
+
* Merges with runtime defaults (actual class references) for fallback behavior
|
|
37
48
|
*/
|
|
38
49
|
createCaches(): Promise<CustomCacheStorage>;
|
|
50
|
+
/**
|
|
51
|
+
* Create directory storage using config from shovel.json
|
|
52
|
+
* Merges with runtime defaults (actual class references) for fallback behavior
|
|
53
|
+
*/
|
|
54
|
+
createDirectories(): Promise<CustomDirectoryStorage>;
|
|
55
|
+
/**
|
|
56
|
+
* Create logger storage using config from shovel.json
|
|
57
|
+
*/
|
|
58
|
+
createLoggers(): Promise<CustomLoggerStorage>;
|
|
59
|
+
/**
|
|
60
|
+
* Create database storage from declarative config in shovel.json
|
|
61
|
+
*/
|
|
62
|
+
createDatabases(configOverride?: BunPlatformOptions["config"]): CustomDatabaseStorage | undefined;
|
|
39
63
|
/**
|
|
40
64
|
* Create HTTP server using Bun.serve
|
|
41
65
|
*/
|
|
@@ -50,12 +74,48 @@ export declare class BunPlatform extends BasePlatform {
|
|
|
50
74
|
* @param entrypoint - Path to the new entrypoint (hashed filename)
|
|
51
75
|
*/
|
|
52
76
|
reloadWorkers(entrypoint: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Get virtual entry wrapper for Bun
|
|
79
|
+
*
|
|
80
|
+
* @param entryPath - Absolute path to user's entrypoint file
|
|
81
|
+
* @param options - Entry wrapper options
|
|
82
|
+
* @param options.type - "production" (default) or "worker"
|
|
83
|
+
* @param options.outDir - Output directory (required for "worker" type)
|
|
84
|
+
*
|
|
85
|
+
* Returns:
|
|
86
|
+
* - "production": Server entry with Bun.serve and reusePort
|
|
87
|
+
* - "worker": Worker entry that sets up runtime and message loop
|
|
88
|
+
*/
|
|
89
|
+
getEntryWrapper(entryPath: string, options?: EntryWrapperOptions): string;
|
|
90
|
+
/**
|
|
91
|
+
* Get Bun-specific esbuild configuration
|
|
92
|
+
*
|
|
93
|
+
* Note: Bun natively supports import.meta.env, so no define alias is needed.
|
|
94
|
+
* We use platform: "node" since Bun is Node-compatible for module resolution.
|
|
95
|
+
*/
|
|
96
|
+
getESBuildConfig(): PlatformESBuildConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Get Bun-specific defaults for config generation
|
|
99
|
+
*
|
|
100
|
+
* Provides default directories (server, public, tmp) that work
|
|
101
|
+
* out of the box for Bun deployments.
|
|
102
|
+
*/
|
|
103
|
+
getDefaults(): PlatformDefaults;
|
|
53
104
|
/**
|
|
54
105
|
* Dispose of platform resources
|
|
55
106
|
*/
|
|
56
107
|
dispose(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Get the OS temp directory (Bun-specific implementation using node:os)
|
|
110
|
+
*/
|
|
111
|
+
tmpdir(): string;
|
|
57
112
|
}
|
|
58
113
|
/**
|
|
59
114
|
* Default export for easy importing
|
|
60
115
|
*/
|
|
61
116
|
export default BunPlatform;
|
|
117
|
+
/**
|
|
118
|
+
* Platform's default cache implementation.
|
|
119
|
+
* Re-exported so config can reference: { module: "@b9g/platform-bun", export: "DefaultCache" }
|
|
120
|
+
*/
|
|
121
|
+
export { MemoryCache as DefaultCache } from "@b9g/cache/memory";
|
package/src/index.js
CHANGED
|
@@ -1,34 +1,174 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
2
|
// src/index.ts
|
|
3
|
+
import { builtinModules } from "node:module";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
3
5
|
import {
|
|
4
6
|
BasePlatform,
|
|
5
7
|
ServiceWorkerPool,
|
|
6
8
|
SingleThreadedRuntime,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
CustomLoggerStorage,
|
|
10
|
+
CustomDatabaseStorage,
|
|
11
|
+
createDatabaseFactory
|
|
9
12
|
} from "@b9g/platform";
|
|
13
|
+
import {
|
|
14
|
+
createCacheFactory,
|
|
15
|
+
createDirectoryFactory
|
|
16
|
+
} from "@b9g/platform/runtime";
|
|
10
17
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
18
|
+
import { MemoryCache } from "@b9g/cache/memory";
|
|
19
|
+
import { CustomDirectoryStorage } from "@b9g/filesystem";
|
|
20
|
+
import { NodeFSDirectory } from "@b9g/filesystem/node-fs";
|
|
11
21
|
import { InternalServerError, isHTTPError } from "@b9g/http-errors";
|
|
12
|
-
import * as Path from "path";
|
|
13
22
|
import { getLogger } from "@logtape/logtape";
|
|
14
|
-
|
|
23
|
+
import * as Path from "path";
|
|
24
|
+
import { MemoryCache as MemoryCache2 } from "@b9g/cache/memory";
|
|
25
|
+
var entryTemplate = `// Bun Production Server Entry
|
|
26
|
+
import {tmpdir} from "os"; // For [tmpdir] config expressions
|
|
27
|
+
import {getLogger} from "@logtape/logtape";
|
|
28
|
+
import {configureLogging} from "@b9g/platform/runtime";
|
|
29
|
+
import {config} from "shovel:config"; // Virtual module - resolved at build time
|
|
30
|
+
import BunPlatform from "@b9g/platform-bun";
|
|
31
|
+
|
|
32
|
+
// Configure logging before anything else
|
|
33
|
+
await configureLogging(config.logging);
|
|
34
|
+
|
|
35
|
+
const logger = getLogger(["shovel", "platform"]);
|
|
36
|
+
|
|
37
|
+
// Configuration from shovel:config
|
|
38
|
+
const PORT = config.port;
|
|
39
|
+
const HOST = config.host;
|
|
40
|
+
const WORKERS = config.workers;
|
|
41
|
+
|
|
42
|
+
// Use explicit marker instead of Bun.isMainThread
|
|
43
|
+
// This handles the edge case where Shovel's build output is embedded in another worker
|
|
44
|
+
const isShovelWorker = process.env.SHOVEL_SPAWNED_WORKER === "1";
|
|
45
|
+
|
|
46
|
+
if (isShovelWorker) {
|
|
47
|
+
// Worker thread: runs BOTH server AND ServiceWorker
|
|
48
|
+
const platform = new BunPlatform({port: PORT, host: HOST, workers: 1});
|
|
49
|
+
const userCodePath = new URL("./server.js", import.meta.url).pathname;
|
|
50
|
+
const serviceWorker = await platform.loadServiceWorker(userCodePath);
|
|
51
|
+
|
|
52
|
+
Bun.serve({
|
|
53
|
+
port: PORT,
|
|
54
|
+
hostname: HOST,
|
|
55
|
+
// Only need reusePort for multi-worker (multiple listeners on same port)
|
|
56
|
+
reusePort: WORKERS > 1,
|
|
57
|
+
fetch: serviceWorker.handleRequest,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Signal ready to main thread
|
|
61
|
+
postMessage({type: "ready", thread: Bun.threadId});
|
|
62
|
+
logger.info("Worker started", {port: PORT, thread: Bun.threadId});
|
|
63
|
+
} else {
|
|
64
|
+
// Main thread: supervisor only - ALWAYS spawn workers (even for workers:1)
|
|
65
|
+
// This ensures ServiceWorker code always runs in a worker thread for dev/prod parity
|
|
66
|
+
|
|
67
|
+
// Port availability check - fail fast if port is in use
|
|
68
|
+
// Prevents accidental port sharing with other processes
|
|
69
|
+
const checkPort = async () => {
|
|
70
|
+
try {
|
|
71
|
+
const testServer = Bun.serve({port: PORT, hostname: HOST, fetch: () => new Response()});
|
|
72
|
+
testServer.stop();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
logger.error("Port unavailable", {port: PORT, host: HOST, error: err});
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
await checkPort();
|
|
79
|
+
|
|
80
|
+
let shuttingDown = false;
|
|
81
|
+
const workers = [];
|
|
82
|
+
let readyCount = 0;
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < WORKERS; i++) {
|
|
85
|
+
const worker = new Worker(import.meta.path, {
|
|
86
|
+
env: {...process.env, SHOVEL_SPAWNED_WORKER: "1"},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
worker.onmessage = (event) => {
|
|
90
|
+
if (event.data.type === "ready") {
|
|
91
|
+
readyCount++;
|
|
92
|
+
if (readyCount === WORKERS) {
|
|
93
|
+
logger.info("All workers ready", {count: WORKERS, port: PORT});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
worker.onerror = (error) => {
|
|
99
|
+
logger.error("Worker error", {error: error.message});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// If a worker crashes, fail fast - let process supervisor handle restarts
|
|
103
|
+
worker.addEventListener("close", () => {
|
|
104
|
+
if (shuttingDown) return;
|
|
105
|
+
logger.error("Worker crashed, exiting");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
workers.push(worker);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
logger.info("Spawned workers", {count: WORKERS, port: PORT});
|
|
113
|
+
|
|
114
|
+
// Graceful shutdown
|
|
115
|
+
const shutdown = async () => {
|
|
116
|
+
shuttingDown = true;
|
|
117
|
+
logger.info("Shutting down workers");
|
|
118
|
+
for (const worker of workers) {
|
|
119
|
+
worker.terminate();
|
|
120
|
+
}
|
|
121
|
+
process.exit(0);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
process.on("SIGINT", shutdown);
|
|
125
|
+
process.on("SIGTERM", shutdown);
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
var workerEntryTemplate = `// Worker Entry for ServiceWorkerPool
|
|
129
|
+
// This file sets up the ServiceWorker runtime and message loop
|
|
130
|
+
import {tmpdir} from "os"; // For [tmpdir] config expressions
|
|
131
|
+
import {config} from "shovel:config";
|
|
132
|
+
import {initWorkerRuntime, startWorkerMessageLoop, configureLogging} from "@b9g/platform/runtime";
|
|
133
|
+
|
|
134
|
+
// Configure logging before anything else
|
|
135
|
+
await configureLogging(config.logging);
|
|
136
|
+
|
|
137
|
+
// Initialize the worker runtime (installs ServiceWorker globals)
|
|
138
|
+
// Platform defaults and paths are already resolved at build time
|
|
139
|
+
const {registration, databases} = await initWorkerRuntime({config});
|
|
140
|
+
|
|
141
|
+
// Import user code (registers event handlers via addEventListener)
|
|
142
|
+
// Must use dynamic import to ensure globals are installed first
|
|
143
|
+
await import("__USER_ENTRY__");
|
|
144
|
+
|
|
145
|
+
// Run ServiceWorker lifecycle
|
|
146
|
+
await registration.install();
|
|
147
|
+
await registration.activate();
|
|
148
|
+
|
|
149
|
+
// Start the message loop (handles request/response messages from main thread)
|
|
150
|
+
// Pass databases so they can be closed on graceful shutdown
|
|
151
|
+
startWorkerMessageLoop({registration, databases});
|
|
152
|
+
`;
|
|
153
|
+
var logger = getLogger(["shovel", "platform"]);
|
|
15
154
|
var BunPlatform = class extends BasePlatform {
|
|
16
155
|
name;
|
|
17
156
|
#options;
|
|
18
157
|
#workerPool;
|
|
19
158
|
#singleThreadedRuntime;
|
|
20
159
|
#cacheStorage;
|
|
21
|
-
#
|
|
160
|
+
#directoryStorage;
|
|
161
|
+
#databaseStorage;
|
|
22
162
|
constructor(options = {}) {
|
|
23
163
|
super(options);
|
|
24
164
|
this.name = "bun";
|
|
25
165
|
const cwd = options.cwd || process.cwd();
|
|
26
|
-
this.#config = loadConfig(cwd);
|
|
27
166
|
this.#options = {
|
|
28
|
-
port: options.port ??
|
|
29
|
-
host: options.host ??
|
|
167
|
+
port: options.port ?? 3e3,
|
|
168
|
+
host: options.host ?? "localhost",
|
|
169
|
+
workers: options.workers ?? 1,
|
|
30
170
|
cwd,
|
|
31
|
-
|
|
171
|
+
config: options.config
|
|
32
172
|
};
|
|
33
173
|
}
|
|
34
174
|
/**
|
|
@@ -47,46 +187,96 @@ var BunPlatform = class extends BasePlatform {
|
|
|
47
187
|
this.#workerPool = pool;
|
|
48
188
|
}
|
|
49
189
|
/**
|
|
50
|
-
* Create cache storage
|
|
51
|
-
*
|
|
190
|
+
* Create cache storage using config from shovel.json
|
|
191
|
+
* Merges with runtime defaults (actual class references) for fallback behavior
|
|
52
192
|
*/
|
|
53
193
|
async createCaches() {
|
|
54
|
-
|
|
194
|
+
const runtimeDefaults = {
|
|
195
|
+
default: { impl: MemoryCache }
|
|
196
|
+
};
|
|
197
|
+
const userCaches = this.#options.config?.caches ?? {};
|
|
198
|
+
const configs = {};
|
|
199
|
+
const allNames = /* @__PURE__ */ new Set([
|
|
200
|
+
...Object.keys(runtimeDefaults),
|
|
201
|
+
...Object.keys(userCaches)
|
|
202
|
+
]);
|
|
203
|
+
for (const name of allNames) {
|
|
204
|
+
configs[name] = { ...runtimeDefaults[name], ...userCaches[name] };
|
|
205
|
+
}
|
|
206
|
+
return new CustomCacheStorage(createCacheFactory({ configs }));
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create directory storage using config from shovel.json
|
|
210
|
+
* Merges with runtime defaults (actual class references) for fallback behavior
|
|
211
|
+
*/
|
|
212
|
+
async createDirectories() {
|
|
213
|
+
const runtimeDefaults = {
|
|
214
|
+
server: { impl: NodeFSDirectory, path: this.#options.cwd },
|
|
215
|
+
public: { impl: NodeFSDirectory, path: this.#options.cwd },
|
|
216
|
+
tmp: { impl: NodeFSDirectory, path: tmpdir() }
|
|
217
|
+
};
|
|
218
|
+
const userDirs = this.#options.config?.directories ?? {};
|
|
219
|
+
const configs = {};
|
|
220
|
+
const allNames = /* @__PURE__ */ new Set([
|
|
221
|
+
...Object.keys(runtimeDefaults),
|
|
222
|
+
...Object.keys(userDirs)
|
|
223
|
+
]);
|
|
224
|
+
for (const name of allNames) {
|
|
225
|
+
configs[name] = { ...runtimeDefaults[name], ...userDirs[name] };
|
|
226
|
+
}
|
|
227
|
+
return new CustomDirectoryStorage(createDirectoryFactory(configs));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Create logger storage using config from shovel.json
|
|
231
|
+
*/
|
|
232
|
+
async createLoggers() {
|
|
233
|
+
return new CustomLoggerStorage((categories) => getLogger(categories));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create database storage from declarative config in shovel.json
|
|
237
|
+
*/
|
|
238
|
+
createDatabases(configOverride) {
|
|
239
|
+
const config = configOverride ?? this.#options.config;
|
|
240
|
+
if (config?.databases && Object.keys(config.databases).length > 0) {
|
|
241
|
+
const factory = createDatabaseFactory(config.databases);
|
|
242
|
+
return new CustomDatabaseStorage(factory);
|
|
243
|
+
}
|
|
244
|
+
return void 0;
|
|
55
245
|
}
|
|
56
246
|
/**
|
|
57
247
|
* Create HTTP server using Bun.serve
|
|
58
248
|
*/
|
|
59
249
|
createServer(handler, options = {}) {
|
|
60
|
-
const
|
|
250
|
+
const requestedPort = options.port ?? this.#options.port;
|
|
61
251
|
const hostname = options.host ?? this.#options.host;
|
|
62
252
|
const server = Bun.serve({
|
|
63
|
-
port,
|
|
253
|
+
port: requestedPort,
|
|
64
254
|
hostname,
|
|
65
255
|
async fetch(request) {
|
|
66
256
|
try {
|
|
67
257
|
return await handler(request);
|
|
68
258
|
} catch (error) {
|
|
69
259
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
70
|
-
logger.error("Request error", {
|
|
71
|
-
error: err.message,
|
|
72
|
-
stack: err.stack
|
|
73
|
-
});
|
|
260
|
+
logger.error("Request error: {error}", { error: err });
|
|
74
261
|
const httpError = isHTTPError(error) ? error : new InternalServerError(err.message, { cause: err });
|
|
75
262
|
const isDev = import.meta.env?.MODE !== "production";
|
|
76
263
|
return httpError.toResponse(isDev);
|
|
77
264
|
}
|
|
78
265
|
}
|
|
79
266
|
});
|
|
267
|
+
const actualPort = server.port;
|
|
80
268
|
return {
|
|
81
269
|
async listen() {
|
|
82
|
-
logger.info("Bun server running", {
|
|
270
|
+
logger.info("Bun server running", {
|
|
271
|
+
url: `http://${hostname}:${actualPort}`
|
|
272
|
+
});
|
|
83
273
|
},
|
|
84
274
|
async close() {
|
|
85
275
|
server.stop();
|
|
86
276
|
},
|
|
87
|
-
address: () => ({ port, host: hostname }),
|
|
277
|
+
address: () => ({ port: actualPort, host: hostname }),
|
|
88
278
|
get url() {
|
|
89
|
-
return `http://${hostname}:${
|
|
279
|
+
return `http://${hostname}:${actualPort}`;
|
|
90
280
|
},
|
|
91
281
|
get ready() {
|
|
92
282
|
return true;
|
|
@@ -98,7 +288,7 @@ var BunPlatform = class extends BasePlatform {
|
|
|
98
288
|
* Uses native Web Workers with the common WorkerPool
|
|
99
289
|
*/
|
|
100
290
|
async loadServiceWorker(entrypoint, options = {}) {
|
|
101
|
-
const workerCount = options.workerCount ?? this.#
|
|
291
|
+
const workerCount = options.workerCount ?? this.#options.workers;
|
|
102
292
|
if (workerCount === 1 && !options.hotReload) {
|
|
103
293
|
return this.#loadServiceWorkerDirect(entrypoint, options);
|
|
104
294
|
}
|
|
@@ -110,9 +300,55 @@ var BunPlatform = class extends BasePlatform {
|
|
|
110
300
|
*/
|
|
111
301
|
async #loadServiceWorkerDirect(entrypoint, _options) {
|
|
112
302
|
const entryPath = Path.resolve(this.#options.cwd, entrypoint);
|
|
113
|
-
|
|
303
|
+
let config = this.#options.config;
|
|
304
|
+
const configPath = Path.join(Path.dirname(entryPath), "config.js");
|
|
305
|
+
try {
|
|
306
|
+
const configModule = await import(configPath);
|
|
307
|
+
config = configModule.config ?? config;
|
|
308
|
+
} catch (err) {
|
|
309
|
+
logger.debug`Using platform config (no config.js): ${err}`;
|
|
310
|
+
}
|
|
114
311
|
if (!this.#cacheStorage) {
|
|
115
|
-
|
|
312
|
+
const runtimeCacheDefaults = {
|
|
313
|
+
default: { impl: MemoryCache }
|
|
314
|
+
};
|
|
315
|
+
const userCaches = config?.caches ?? {};
|
|
316
|
+
const cacheConfigs = {};
|
|
317
|
+
const allCacheNames = /* @__PURE__ */ new Set([
|
|
318
|
+
...Object.keys(runtimeCacheDefaults),
|
|
319
|
+
...Object.keys(userCaches)
|
|
320
|
+
]);
|
|
321
|
+
for (const name of allCacheNames) {
|
|
322
|
+
cacheConfigs[name] = {
|
|
323
|
+
...runtimeCacheDefaults[name],
|
|
324
|
+
...userCaches[name]
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
this.#cacheStorage = new CustomCacheStorage(
|
|
328
|
+
createCacheFactory({ configs: cacheConfigs })
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (!this.#directoryStorage) {
|
|
332
|
+
const runtimeDirDefaults = {
|
|
333
|
+
server: { impl: NodeFSDirectory },
|
|
334
|
+
public: { impl: NodeFSDirectory },
|
|
335
|
+
tmp: { impl: NodeFSDirectory }
|
|
336
|
+
};
|
|
337
|
+
const userDirs = config?.directories ?? {};
|
|
338
|
+
const dirConfigs = {};
|
|
339
|
+
const allDirNames = /* @__PURE__ */ new Set([
|
|
340
|
+
...Object.keys(runtimeDirDefaults),
|
|
341
|
+
...Object.keys(userDirs)
|
|
342
|
+
]);
|
|
343
|
+
for (const name of allDirNames) {
|
|
344
|
+
dirConfigs[name] = { ...runtimeDirDefaults[name], ...userDirs[name] };
|
|
345
|
+
}
|
|
346
|
+
this.#directoryStorage = new CustomDirectoryStorage(
|
|
347
|
+
createDirectoryFactory(dirConfigs)
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
if (!this.#databaseStorage) {
|
|
351
|
+
this.#databaseStorage = this.createDatabases(config);
|
|
116
352
|
}
|
|
117
353
|
if (this.#singleThreadedRuntime) {
|
|
118
354
|
await this.#singleThreadedRuntime.terminate();
|
|
@@ -123,12 +359,13 @@ var BunPlatform = class extends BasePlatform {
|
|
|
123
359
|
}
|
|
124
360
|
logger.info("Creating single-threaded ServiceWorker runtime", { entryPath });
|
|
125
361
|
this.#singleThreadedRuntime = new SingleThreadedRuntime({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
362
|
+
caches: this.#cacheStorage,
|
|
363
|
+
directories: this.#directoryStorage,
|
|
364
|
+
databases: this.#databaseStorage,
|
|
365
|
+
loggers: new CustomLoggerStorage((cats) => getLogger(cats))
|
|
129
366
|
});
|
|
130
367
|
await this.#singleThreadedRuntime.init();
|
|
131
|
-
await this.#singleThreadedRuntime.
|
|
368
|
+
await this.#singleThreadedRuntime.load(entryPath);
|
|
132
369
|
const runtime = this.#singleThreadedRuntime;
|
|
133
370
|
const platform = this;
|
|
134
371
|
const instance = {
|
|
@@ -185,11 +422,9 @@ var BunPlatform = class extends BasePlatform {
|
|
|
185
422
|
this.#workerPool = new ServiceWorkerPool(
|
|
186
423
|
poolOptions,
|
|
187
424
|
entryPath,
|
|
188
|
-
this.#cacheStorage
|
|
189
|
-
this.#config
|
|
425
|
+
this.#cacheStorage
|
|
190
426
|
);
|
|
191
427
|
await this.#workerPool.init();
|
|
192
|
-
await this.#workerPool.reloadWorkers(entryPath);
|
|
193
428
|
const workerPool = this.#workerPool;
|
|
194
429
|
const platform = this;
|
|
195
430
|
const instance = {
|
|
@@ -230,9 +465,72 @@ var BunPlatform = class extends BasePlatform {
|
|
|
230
465
|
if (this.#workerPool) {
|
|
231
466
|
await this.#workerPool.reloadWorkers(entrypoint);
|
|
232
467
|
} else if (this.#singleThreadedRuntime) {
|
|
233
|
-
await this.#singleThreadedRuntime.
|
|
468
|
+
await this.#singleThreadedRuntime.load(entrypoint);
|
|
234
469
|
}
|
|
235
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Get virtual entry wrapper for Bun
|
|
473
|
+
*
|
|
474
|
+
* @param entryPath - Absolute path to user's entrypoint file
|
|
475
|
+
* @param options - Entry wrapper options
|
|
476
|
+
* @param options.type - "production" (default) or "worker"
|
|
477
|
+
* @param options.outDir - Output directory (required for "worker" type)
|
|
478
|
+
*
|
|
479
|
+
* Returns:
|
|
480
|
+
* - "production": Server entry with Bun.serve and reusePort
|
|
481
|
+
* - "worker": Worker entry that sets up runtime and message loop
|
|
482
|
+
*/
|
|
483
|
+
getEntryWrapper(entryPath, options) {
|
|
484
|
+
if (options?.type === "worker") {
|
|
485
|
+
return workerEntryTemplate.replace("__USER_ENTRY__", entryPath);
|
|
486
|
+
}
|
|
487
|
+
return entryTemplate;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Get Bun-specific esbuild configuration
|
|
491
|
+
*
|
|
492
|
+
* Note: Bun natively supports import.meta.env, so no define alias is needed.
|
|
493
|
+
* We use platform: "node" since Bun is Node-compatible for module resolution.
|
|
494
|
+
*/
|
|
495
|
+
getESBuildConfig() {
|
|
496
|
+
return {
|
|
497
|
+
platform: "node",
|
|
498
|
+
external: ["node:*", "bun", "bun:*", ...builtinModules]
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Get Bun-specific defaults for config generation
|
|
503
|
+
*
|
|
504
|
+
* Provides default directories (server, public, tmp) that work
|
|
505
|
+
* out of the box for Bun deployments.
|
|
506
|
+
*/
|
|
507
|
+
getDefaults() {
|
|
508
|
+
return {
|
|
509
|
+
caches: {
|
|
510
|
+
default: {
|
|
511
|
+
module: "@b9g/cache/memory",
|
|
512
|
+
export: "MemoryCache"
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
directories: {
|
|
516
|
+
server: {
|
|
517
|
+
module: "@b9g/filesystem/node-fs",
|
|
518
|
+
export: "NodeFSDirectory",
|
|
519
|
+
path: "[outdir]/server"
|
|
520
|
+
},
|
|
521
|
+
public: {
|
|
522
|
+
module: "@b9g/filesystem/node-fs",
|
|
523
|
+
export: "NodeFSDirectory",
|
|
524
|
+
path: "[outdir]/public"
|
|
525
|
+
},
|
|
526
|
+
tmp: {
|
|
527
|
+
module: "@b9g/filesystem/node-fs",
|
|
528
|
+
export: "NodeFSDirectory",
|
|
529
|
+
path: "[tmpdir]"
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
236
534
|
/**
|
|
237
535
|
* Dispose of platform resources
|
|
238
536
|
*/
|
|
@@ -249,10 +547,24 @@ var BunPlatform = class extends BasePlatform {
|
|
|
249
547
|
await this.#cacheStorage.dispose();
|
|
250
548
|
this.#cacheStorage = void 0;
|
|
251
549
|
}
|
|
550
|
+
if (this.#databaseStorage) {
|
|
551
|
+
await this.#databaseStorage.closeAll();
|
|
552
|
+
this.#databaseStorage = void 0;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// =========================================================================
|
|
556
|
+
// Config Expression Method Overrides
|
|
557
|
+
// =========================================================================
|
|
558
|
+
/**
|
|
559
|
+
* Get the OS temp directory (Bun-specific implementation using node:os)
|
|
560
|
+
*/
|
|
561
|
+
tmpdir() {
|
|
562
|
+
return tmpdir();
|
|
252
563
|
}
|
|
253
564
|
};
|
|
254
565
|
var src_default = BunPlatform;
|
|
255
566
|
export {
|
|
256
567
|
BunPlatform,
|
|
568
|
+
MemoryCache2 as DefaultCache,
|
|
257
569
|
src_default as default
|
|
258
570
|
};
|