@b9g/platform 0.1.12 → 0.1.13

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 CHANGED
@@ -1,28 +1,34 @@
1
1
  {
2
2
  "name": "@b9g/platform",
3
- "version": "0.1.12",
4
- "description": "ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.",
3
+ "version": "0.1.13",
4
+ "description": "The portable meta-framework built on web standards.",
5
5
  "keywords": [
6
- "serviceworker",
6
+ "service-worker",
7
7
  "universal",
8
8
  "deployment",
9
9
  "platform",
10
- "registry",
11
10
  "node",
12
11
  "bun",
13
12
  "cloudflare",
14
13
  "workers",
14
+ "cache",
15
+ "framework",
16
+ "meta-framework",
17
+ "esbuild",
15
18
  "shovel"
16
19
  ],
17
20
  "dependencies": {
18
- "@b9g/async-context": "^0.1.4",
19
- "@b9g/cache": "^0.1.5",
21
+ "@b9g/async-context": "^0.2.0-beta.0",
22
+ "@b9g/cache": "^0.2.0-beta.0",
20
23
  "@b9g/filesystem": "^0.1.7",
24
+ "@b9g/zen": "^0.1.6",
21
25
  "@logtape/logtape": "^1.2.0"
22
26
  },
23
27
  "devDependencies": {
24
- "@b9g/libuild": "^0.1.18",
25
- "bun-types": "latest"
28
+ "@b9g/libuild": "^0.1.20",
29
+ "@b9g/node-webworker": "^0.2.0-beta.1",
30
+ "@b9g/platform-bun": "^0.1.10",
31
+ "@b9g/platform-node": "^0.1.12"
26
32
  },
27
33
  "peerDependencies": {
28
34
  "@logtape/file": "^1.0.0",
@@ -65,14 +71,6 @@
65
71
  "types": "./src/runtime.d.ts",
66
72
  "import": "./src/runtime.js"
67
73
  },
68
- "./worker": {
69
- "types": "./src/worker.d.ts",
70
- "import": "./src/worker.js"
71
- },
72
- "./worker.js": {
73
- "types": "./src/worker.d.ts",
74
- "import": "./src/worker.js"
75
- },
76
74
  "./index": {
77
75
  "types": "./src/index.d.ts",
78
76
  "import": "./src/index.js"
@@ -80,6 +78,16 @@
80
78
  "./index.js": {
81
79
  "types": "./src/index.d.ts",
82
80
  "import": "./src/index.js"
81
+ },
82
+ "./globals.d.ts": "./src/globals.d.ts",
83
+ "./shovel-config.d.ts": "./src/shovel-config.d.ts",
84
+ "./config": {
85
+ "types": "./src/config.d.ts",
86
+ "import": "./src/config.js"
87
+ },
88
+ "./config.js": {
89
+ "types": "./src/config.d.ts",
90
+ "import": "./src/config.js"
83
91
  }
84
92
  }
85
93
  }
@@ -0,0 +1,24 @@
1
+ /// <reference path="./globals.d.ts" />
2
+ /// <reference path="./shovel-config.d.ts" />
3
+ /**
4
+ * Config Validation Utilities
5
+ *
6
+ * Helpers for validating config objects at runtime.
7
+ */
8
+ /**
9
+ * Error thrown when config validation fails
10
+ */
11
+ export declare class ConfigValidationError extends Error {
12
+ readonly path: string;
13
+ readonly issue: "undefined" | "NaN";
14
+ constructor(path: string, issue: "undefined" | "NaN");
15
+ }
16
+ /**
17
+ * Validate that a config object has no undefined or NaN values.
18
+ * Call this at runtime to fail fast on missing env vars.
19
+ *
20
+ * @param config - The config object to validate
21
+ * @param path - Current path for error messages (used in recursion)
22
+ * @throws ConfigValidationError if any value is undefined or NaN
23
+ */
24
+ export declare function validateConfig(config: Record<string, unknown>, path?: string): void;
package/src/config.js ADDED
@@ -0,0 +1,29 @@
1
+ /// <reference types="./config.d.ts" />
2
+ // src/config.ts
3
+ var ConfigValidationError = class extends Error {
4
+ constructor(path, issue) {
5
+ const message = issue === "undefined" ? `Config "${path}" is undefined. Ensure required environment variables are set.` : `Config "${path}" is NaN. Ensure the environment variable contains a valid number.`;
6
+ super(message);
7
+ this.path = path;
8
+ this.issue = issue;
9
+ this.name = "ConfigValidationError";
10
+ }
11
+ };
12
+ function validateConfig(config, path = "") {
13
+ for (const [key, value] of Object.entries(config)) {
14
+ const fullPath = path ? `${path}.${key}` : key;
15
+ if (value === void 0) {
16
+ throw new ConfigValidationError(fullPath, "undefined");
17
+ }
18
+ if (typeof value === "number" && Number.isNaN(value)) {
19
+ throw new ConfigValidationError(fullPath, "NaN");
20
+ }
21
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
22
+ validateConfig(value, fullPath);
23
+ }
24
+ }
25
+ }
26
+ export {
27
+ ConfigValidationError,
28
+ validateConfig
29
+ };
@@ -0,0 +1,119 @@
1
+ /// <reference lib="webworker" />
2
+
3
+ /**
4
+ * Global type declarations for Shovel ServiceWorker environment.
5
+ *
6
+ * These types augment the global scope with Shovel-specific APIs
7
+ * that are installed by ServiceWorkerGlobals.
8
+ *
9
+ * Usage: Include this file in your tsconfig.json "include" array
10
+ * or reference it with /// <reference types="@b9g/platform/globals" />
11
+ */
12
+
13
+ import type {Logger} from "@logtape/logtape";
14
+ import type {DirectoryStorage} from "@b9g/filesystem";
15
+
16
+ declare global {
17
+ /**
18
+ * Logger storage API for accessing named loggers.
19
+ * @example const logger = self.loggers.get(["app"]);
20
+ * @example const dbLogger = self.loggers.get(["app", "db"]);
21
+ */
22
+ interface LoggerStorage {
23
+ get(categories: string[]): Logger;
24
+ }
25
+
26
+ /**
27
+ * Upgrade event passed to onUpgrade callback during database.open().
28
+ */
29
+ interface DatabaseUpgradeEvent {
30
+ /** The database being upgraded */
31
+ db: unknown;
32
+ /** Previous database version (0 if new) */
33
+ oldVersion: number;
34
+ /** Target version being opened */
35
+ newVersion: number;
36
+ /** Register a promise that must complete before open() resolves */
37
+ waitUntil(promise: Promise<unknown>): void;
38
+ }
39
+
40
+ /**
41
+ * Database storage API for accessing named database instances.
42
+ *
43
+ * @example
44
+ * // In activate - open with migrations
45
+ * await self.databases.open("main", 2, (e) => {
46
+ * e.waitUntil(runMigrations(e));
47
+ * });
48
+ *
49
+ * // In fetch - get opened database (sync)
50
+ * const db = self.databases.get("main");
51
+ */
52
+ interface DatabaseStorage {
53
+ /** Open a database at a specific version, running migrations if needed */
54
+ open(
55
+ name: string,
56
+ version: number,
57
+ onUpgrade?: (event: DatabaseUpgradeEvent) => void,
58
+ ): Promise<unknown>;
59
+ /** Get an already-opened database (throws if not opened) */
60
+ get(name: string): unknown;
61
+ /** Close a specific database */
62
+ close(name: string): Promise<void>;
63
+ /** Close all databases */
64
+ closeAll(): Promise<void>;
65
+ }
66
+
67
+ /**
68
+ * Directory storage API for accessing named directories.
69
+ * @example const uploads = await directories.open("uploads");
70
+ */
71
+ var directories: DirectoryStorage;
72
+
73
+ /**
74
+ * Logger storage API for accessing named loggers.
75
+ * @example const logger = self.loggers.get(["app"]);
76
+ * @example const dbLogger = self.loggers.get(["app", "db"]);
77
+ */
78
+ var loggers: LoggerStorage;
79
+
80
+ /**
81
+ * Database storage API for accessing named database instances.
82
+ * @example const db = self.databases.get("main");
83
+ */
84
+ var databases: DatabaseStorage;
85
+
86
+ /**
87
+ * Environment variables available via import.meta.env
88
+ * Works across all platforms (Node/Bun via esbuild shim, Cloudflare natively)
89
+ */
90
+ interface ImportMetaEnv {
91
+ readonly [key: string]: string | undefined;
92
+ }
93
+
94
+ interface ImportMeta {
95
+ readonly env: ImportMetaEnv;
96
+ }
97
+
98
+ /**
99
+ * Augment WorkerGlobalScopeEventMap with ServiceWorker events.
100
+ *
101
+ * TypeScript's lib.webworker.d.ts declares `self` as `WorkerGlobalScope`, not
102
+ * `ServiceWorkerGlobalScope`. This means `self.addEventListener("fetch", ...)`
103
+ * doesn't know about FetchEvent. See: https://github.com/microsoft/TypeScript/issues/14877
104
+ *
105
+ * Rather than trying to redeclare `self` (which causes conflicts), we augment
106
+ * the base WorkerGlobalScopeEventMap to include ServiceWorker-specific events.
107
+ * This allows `self.addEventListener("fetch", (event) => ...)` to correctly
108
+ * infer `event` as `FetchEvent`.
109
+ */
110
+ interface WorkerGlobalScopeEventMap {
111
+ fetch: FetchEvent;
112
+ install: ExtendableEvent;
113
+ activate: ExtendableEvent;
114
+ message: ExtendableMessageEvent;
115
+ messageerror: MessageEvent;
116
+ }
117
+ }
118
+
119
+ export {};
package/src/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference path="./globals.d.ts" />
2
+ /// <reference path="./shovel-config.d.ts" />
1
3
  /**
2
4
  * @b9g/platform - Platform interface for ServiceWorker entrypoint loading
3
5
  *
@@ -10,7 +12,8 @@
10
12
  * - ServiceWorkerPool for multi-worker execution
11
13
  */
12
14
  import type { DirectoryStorage } from "@b9g/filesystem";
13
- import { CustomLoggerStorage, type LoggerStorage } from "./runtime.js";
15
+ import { CustomLoggerStorage, type LoggerStorage, type DatabaseStorage } from "./runtime.js";
16
+ export { validateConfig, ConfigValidationError } from "./config.js";
14
17
  /**
15
18
  * Platform configuration
16
19
  * Extended by platform-specific implementations (NodePlatformOptions, etc.)
@@ -25,13 +28,6 @@ export interface ServerOptions {
25
28
  port?: number;
26
29
  /** Host to bind to */
27
30
  host?: string;
28
- /** Development mode settings */
29
- development?: {
30
- /** Source maps support */
31
- sourceMaps?: boolean;
32
- /** Verbose logging */
33
- verbose?: boolean;
34
- };
35
31
  }
36
32
  /**
37
33
  * Request handler function (Web Fetch API compatible)
@@ -85,14 +81,24 @@ export interface ServiceWorkerInstance {
85
81
  }
86
82
  /**
87
83
  * Options for getEntryWrapper()
88
- * Reserved for future platform-specific options.
89
84
  */
90
85
  export interface EntryWrapperOptions {
86
+ /**
87
+ * Type of entry wrapper to generate:
88
+ * - "production": Production server entry (default) - runs the server directly
89
+ * - "worker": Worker entry for ServiceWorkerPool - sets up runtime and message loop
90
+ */
91
+ type?: "production" | "worker";
92
+ /**
93
+ * Output directory for the build. Used to generate absolute paths for
94
+ * directory defaults (server, public). Required for "worker" type.
95
+ */
96
+ outDir?: string;
91
97
  }
92
98
  /**
93
- * Esbuild configuration subset that platforms can customize
99
+ * ESBuild configuration subset that platforms can customize
94
100
  */
95
- export interface PlatformEsbuildConfig {
101
+ export interface PlatformESBuildConfig {
96
102
  /** Target platform: "node" or "browser" */
97
103
  platform?: "node" | "browser" | "neutral";
98
104
  /** Export conditions for package.json resolution */
@@ -112,6 +118,29 @@ export interface PlatformEsbuildConfig {
112
118
  */
113
119
  bundlesUserCodeInline?: boolean;
114
120
  }
121
+ /**
122
+ * Default resource configuration for a named resource (cache, directory, etc.)
123
+ * Used by platforms to define built-in defaults that get merged with user config.
124
+ */
125
+ export interface ResourceDefault {
126
+ /** Module path to import (e.g., "@b9g/cache/memory") */
127
+ module: string;
128
+ /** Named export to use (defaults to "default") */
129
+ export?: string;
130
+ /** Additional options (e.g., path for directories) */
131
+ [key: string]: unknown;
132
+ }
133
+ /**
134
+ * Platform-specific defaults for config generation.
135
+ * These are merged with user config at build time to provide
136
+ * sensible defaults for each platform.
137
+ */
138
+ export interface PlatformDefaults {
139
+ /** Default directory configurations (server, public, tmp, etc.) */
140
+ directories?: Record<string, ResourceDefault>;
141
+ /** Default cache configuration (e.g., memory cache) */
142
+ caches?: Record<string, ResourceDefault>;
143
+ }
115
144
  /**
116
145
  * Platform interface - ServiceWorker entrypoint loader for JavaScript runtimes
117
146
  *
@@ -127,11 +156,6 @@ export interface Platform {
127
156
  * This is where all the platform-specific complexity lives
128
157
  */
129
158
  loadServiceWorker(entrypoint: string, options?: ServiceWorkerOptions): Promise<ServiceWorkerInstance>;
130
- /**
131
- * SUPPORTING UTILITY - Create cache storage
132
- * Returns empty CacheStorage - applications create caches on-demand via caches.open()
133
- */
134
- createCaches(): Promise<CacheStorage>;
135
159
  /**
136
160
  * SUPPORTING UTILITY - Create server instance for this platform
137
161
  */
@@ -157,7 +181,30 @@ export interface Platform {
157
181
  * Returns partial esbuild config that the CLI merges with common settings.
158
182
  * Includes platform target, conditions, externals, and defines.
159
183
  */
160
- getEsbuildConfig(): PlatformEsbuildConfig;
184
+ getESBuildConfig(): PlatformESBuildConfig;
185
+ /**
186
+ * BUILD SUPPORT - Get platform-specific defaults for config generation
187
+ *
188
+ * Returns defaults for directories, caches, etc. that get merged with
189
+ * user config at build time. These are used by generateConfigModule()
190
+ * to create static imports for the default implementations.
191
+ */
192
+ getDefaults(): PlatformDefaults;
193
+ /**
194
+ * Create cache storage for this platform
195
+ * Uses platform-specific defaults, overridable via shovel.json config
196
+ */
197
+ createCaches(): Promise<CacheStorage>;
198
+ /**
199
+ * Create directory storage for this platform
200
+ * Uses platform-specific defaults, overridable via shovel.json config
201
+ */
202
+ createDirectories(): Promise<DirectoryStorage>;
203
+ /**
204
+ * Create logger storage for this platform
205
+ * Uses platform-specific defaults, overridable via shovel.json config
206
+ */
207
+ createLoggers(): Promise<LoggerStorage>;
161
208
  }
162
209
  /**
163
210
  * Platform registry - internal implementation
@@ -217,11 +264,6 @@ export declare abstract class BasePlatform implements Platform {
217
264
  abstract readonly name: string;
218
265
  abstract loadServiceWorker(entrypoint: string, options?: any): Promise<any>;
219
266
  abstract createServer(handler: any, options?: any): any;
220
- /**
221
- * Create cache storage
222
- * Returns empty CacheStorage - applications create caches on-demand via caches.open()
223
- */
224
- createCaches(): Promise<CacheStorage>;
225
267
  /**
226
268
  * Get virtual entry wrapper template for user code
227
269
  * Subclasses must override to provide platform-specific wrappers
@@ -231,7 +273,27 @@ export declare abstract class BasePlatform implements Platform {
231
273
  * Get platform-specific esbuild configuration
232
274
  * Subclasses should override to provide platform-specific config
233
275
  */
234
- abstract getEsbuildConfig(): PlatformEsbuildConfig;
276
+ abstract getESBuildConfig(): PlatformESBuildConfig;
277
+ /**
278
+ * Get platform-specific defaults for config generation
279
+ * Subclasses should override to provide platform-specific defaults
280
+ */
281
+ abstract getDefaults(): PlatformDefaults;
282
+ /**
283
+ * Create cache storage for this platform
284
+ * Subclasses must override to provide platform-specific implementation
285
+ */
286
+ abstract createCaches(): Promise<CacheStorage>;
287
+ /**
288
+ * Create directory storage for this platform
289
+ * Subclasses must override to provide platform-specific implementation
290
+ */
291
+ abstract createDirectories(): Promise<DirectoryStorage>;
292
+ /**
293
+ * Create logger storage for this platform
294
+ * Subclasses must override to provide platform-specific implementation
295
+ */
296
+ abstract createLoggers(): Promise<LoggerStorage>;
235
297
  }
236
298
  /**
237
299
  * Global platform registry
@@ -271,6 +333,8 @@ export interface SingleThreadedRuntimeOptions {
271
333
  caches: CacheStorage;
272
334
  /** Directory storage for the runtime */
273
335
  directories: DirectoryStorage;
336
+ /** Database storage for the runtime */
337
+ databases?: DatabaseStorage;
274
338
  /** Logger storage for the runtime */
275
339
  loggers: LoggerStorage;
276
340
  }
@@ -345,13 +409,8 @@ export interface WorkerResponse extends WorkerMessage {
345
409
  };
346
410
  requestID: number;
347
411
  }
348
- export interface WorkerLoadMessage extends WorkerMessage {
349
- type: "load";
350
- entrypoint: string;
351
- }
352
412
  export interface WorkerReadyMessage extends WorkerMessage {
353
- type: "ready" | "worker-ready";
354
- entrypoint?: string;
413
+ type: "ready";
355
414
  }
356
415
  export interface WorkerErrorMessage extends WorkerMessage {
357
416
  type: "error";
@@ -359,21 +418,21 @@ export interface WorkerErrorMessage extends WorkerMessage {
359
418
  stack?: string;
360
419
  requestID?: number;
361
420
  }
362
- export interface WorkerInitMessage extends WorkerMessage {
363
- type: "init";
364
- config: any;
365
- baseDir: string;
366
- }
367
- export interface WorkerInitializedMessage extends WorkerMessage {
368
- type: "initialized";
369
- }
370
421
  /**
371
422
  * ServiceWorkerPool - manages a pool of ServiceWorker instances
372
- * Handles HTTP request/response routing, cache coordination, and hot reloading
423
+ *
424
+ * With the unified build model, workers are self-contained bundles that:
425
+ * 1. Initialize their own runtime (via initWorkerRuntime)
426
+ * 2. Import user code
427
+ * 3. Run lifecycle events
428
+ * 4. Start message loop (via startWorkerMessageLoop)
429
+ *
430
+ * Hot reload is achieved by terminating old workers and creating new ones
431
+ * with the new bundle path.
373
432
  */
374
433
  export declare class ServiceWorkerPool {
375
434
  #private;
376
- constructor(options?: WorkerPoolOptions, appEntrypoint?: string, cacheStorage?: CacheStorage, config?: any);
435
+ constructor(options: WorkerPoolOptions, appEntrypoint: string, cacheStorage?: CacheStorage);
377
436
  /**
378
437
  * Initialize workers (must be called after construction)
379
438
  */
@@ -383,8 +442,12 @@ export declare class ServiceWorkerPool {
383
442
  */
384
443
  handleRequest(request: Request): Promise<Response>;
385
444
  /**
386
- * Reload ServiceWorker with new entrypoint (hot reload)
387
- * The entrypoint path contains a content hash for cache busting
445
+ * Reload workers with new entrypoint (hot reload)
446
+ *
447
+ * With unified builds, hot reload means:
448
+ * 1. Gracefully shutdown existing workers (close databases, etc.)
449
+ * 2. Terminate workers after resources are closed
450
+ * 3. Create new workers with the new bundle
388
451
  */
389
452
  reloadWorkers(entrypoint: string): Promise<void>;
390
453
  /**
@@ -402,3 +465,4 @@ export declare class ServiceWorkerPool {
402
465
  }
403
466
  export { CustomLoggerStorage, type LoggerStorage };
404
467
  export type { LoggerFactory } from "./runtime.js";
468
+ export { CustomDatabaseStorage, createDatabaseFactory, type DatabaseStorage, type DatabaseConfig, type DatabaseFactory, type DatabaseUpgradeEvent, } from "./runtime.js";