@b9g/platform 0.1.3 → 0.1.5

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 CHANGED
@@ -1,14 +1,14 @@
1
1
  # @b9g/platform
2
2
 
3
- Universal platform abstraction for ServiceWorker-style applications with automatic platform detection and worker thread architecture.
3
+ **ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.**
4
4
 
5
5
  ## Features
6
6
 
7
- - **ServiceWorker Pattern**: Load applications as ServiceWorker entrypoints
8
- - **Multi-Platform**: Node.js, Bun, Cloudflare Workers support
9
- - **Auto-Detection**: Automatic runtime detection with explicit override
10
- - **Worker Architecture**: Multi-worker concurrency with coordinated caching
11
- - **Hot Reloading**: VM module isolation for clean development reloads
7
+ - **ServiceWorkerContainer Registry**: Manage multiple ServiceWorker apps by scope
8
+ - **Complete ServiceWorker API**: Full MDN spec implementation for any JavaScript runtime
9
+ - **Multi-App Orchestration**: Deploy multiple ServiceWorkers with scope-based routing
10
+ - **Universal Platform Support**: Node.js, Bun, Cloudflare Workers with identical APIs
11
+ - **Standards Compliance**: Full ServiceWorker specification compliance
12
12
 
13
13
  ## Installation
14
14
 
@@ -39,14 +39,19 @@ import { createPlatform } from '@b9g/platform';
39
39
  // Auto-detect platform
40
40
  const platform = await createPlatform('auto');
41
41
 
42
- // Load ServiceWorker app
43
- const serviceWorker = await platform.loadServiceWorker('./app.js', {
44
- workerCount: 2,
45
- hotReload: true
46
- });
42
+ // Create ServiceWorker registry
43
+ const container = await platform.createServiceWorkerContainer();
44
+
45
+ // Register multiple ServiceWorker apps by scope
46
+ await container.register('/api-worker.js', { scope: '/api/' });
47
+ await container.register('/admin-worker.js', { scope: '/admin/' });
48
+ await container.register('/app-worker.js', { scope: '/' });
47
49
 
48
- // Create server
49
- const server = platform.createServer(serviceWorker.handleRequest, {
50
+ // Install and activate all ServiceWorkers
51
+ await container.installAll();
52
+
53
+ // Create server that routes to appropriate ServiceWorker
54
+ const server = platform.createServer(container.handleRequest.bind(container), {
50
55
  port: 3000,
51
56
  host: 'localhost'
52
57
  });
@@ -54,18 +59,39 @@ const server = platform.createServer(serviceWorker.handleRequest, {
54
59
  await server.listen();
55
60
  ```
56
61
 
57
- ## ServiceWorker Pattern
62
+ ## ServiceWorker Registry Pattern
58
63
 
59
- Write your app as a ServiceWorker entrypoint:
64
+ Deploy multiple ServiceWorker applications with scope-based routing:
60
65
 
61
66
  ```javascript
62
- // app.js - ServiceWorker-style entrypoint
67
+ // api-worker.js - API ServiceWorker
68
+ import { Router } from '@b9g/router';
69
+
70
+ const router = new Router();
71
+ router.get('/users', () => Response.json({ users: [] }));
72
+ router.get('/posts', () => Response.json({ posts: [] }));
73
+
74
+ addEventListener('install', event => {
75
+ console.log('API service installing...');
76
+ });
77
+
78
+ addEventListener('activate', event => {
79
+ console.log('API service activated!');
80
+ });
81
+
82
+ addEventListener('fetch', event => {
83
+ event.respondWith(router.handler(event.request));
84
+ });
85
+ ```
86
+
87
+ ```javascript
88
+ // app-worker.js - Main app ServiceWorker
63
89
  import { Router } from '@b9g/router';
64
90
 
65
91
  const router = new Router();
66
92
  router.get('/', () => new Response('Hello World!'));
93
+ router.get('/about', () => new Response('About page'));
67
94
 
68
- // ServiceWorker lifecycle events
69
95
  addEventListener('install', event => {
70
96
  console.log('App installing...');
71
97
  });
@@ -74,12 +100,17 @@ addEventListener('activate', event => {
74
100
  console.log('App activated!');
75
101
  });
76
102
 
77
- // Handle fetch events
78
103
  addEventListener('fetch', event => {
79
104
  event.respondWith(router.handler(event.request));
80
105
  });
81
106
  ```
82
107
 
108
+ **Registry automatically routes requests:**
109
+ - `/api/users` → `api-worker.js`
110
+ - `/api/posts` → `api-worker.js`
111
+ - `/` → `app-worker.js`
112
+ - `/about` → `app-worker.js`
113
+
83
114
  ## Platform Detection
84
115
 
85
116
  ```javascript
@@ -107,7 +138,7 @@ displayPlatformInfo(detected);
107
138
  ```javascript
108
139
  const platform = await createPlatform('node');
109
140
 
110
- const serviceWorker = await platform.loadServiceWorker('./app.js', {
141
+ const serviceWorker = await platform.loadServiceWorker('./server.js', {
111
142
  workerCount: 4, // Number of worker threads
112
143
  hotReload: true, // Enable hot reloading
113
144
  caches: {
@@ -126,9 +157,9 @@ const serviceWorker = await platform.loadServiceWorker('./app.js', {
126
157
  ### Node.js Platform
127
158
 
128
159
  ```javascript
129
- import { createNodePlatform } from '@b9g/platform-node';
160
+ import NodePlatform from '@b9g/platform-node';
130
161
 
131
- const platform = await createNodePlatform({
162
+ const platform = new NodePlatform({
132
163
  // Node.js specific options
133
164
  });
134
165
  ```
@@ -136,9 +167,9 @@ const platform = await createNodePlatform({
136
167
  ### Bun Platform
137
168
 
138
169
  ```javascript
139
- import { createBunPlatform } from '@b9g/platform-bun';
170
+ import BunPlatform from '@b9g/platform-bun';
140
171
 
141
- const platform = await createBunPlatform({
172
+ const platform = new BunPlatform({
142
173
  // Bun specific options
143
174
  });
144
175
  ```
@@ -146,13 +177,48 @@ const platform = await createBunPlatform({
146
177
  ### Cloudflare Workers Platform
147
178
 
148
179
  ```javascript
149
- import { createCloudflarePlatform } from '@b9g/platform-cloudflare';
180
+ import CloudflarePlatform from '@b9g/platform-cloudflare';
150
181
 
151
- const platform = await createCloudflarePlatform({
182
+ const platform = new CloudflarePlatform({
152
183
  // Cloudflare specific options
153
184
  });
154
185
  ```
155
186
 
187
+ ## Exports
188
+
189
+ ### Classes
190
+
191
+ - `BasePlatform` - Abstract base class for platform implementations
192
+ - `platformRegistry` - Default platform registry singleton
193
+
194
+ ### Functions
195
+
196
+ - `createPlatform(name, options?)` - Create a platform instance by name
197
+ - `getPlatform(name?)` - Get a registered platform synchronously
198
+ - `getPlatformAsync(name?)` - Get a registered platform asynchronously
199
+ - `detectRuntime()` - Detect current JavaScript runtime ('bun' | 'deno' | 'node')
200
+ - `detectDeploymentPlatform()` - Detect deployment platform (Cloudflare, Vercel, etc.)
201
+ - `detectDevelopmentPlatform()` - Detect development platform
202
+ - `resolvePlatform(options)` - Resolve platform from options
203
+
204
+ ### Types
205
+
206
+ - `Platform` - Platform interface
207
+ - `PlatformConfig` - Platform configuration options
208
+ - `ServerOptions` - Server configuration options
209
+ - `Handler` - Request handler function type
210
+ - `Server` - Server interface
211
+ - `ServiceWorkerOptions` - ServiceWorker loading options
212
+ - `ServiceWorkerInstance` - Loaded ServiceWorker instance
213
+
214
+ ### Re-exports from @b9g/filesystem
215
+
216
+ - `BucketStorage`, `Bucket`, `BucketFactory`, `CustomBucketStorage`
217
+
218
+ ### Re-exports from @b9g/cache
219
+
220
+ - `Cache`, `CacheFactory`, `CacheQueryOptions`, `CustomCacheStorage`
221
+
156
222
  ## API Reference
157
223
 
158
224
  ### Platform Interface
@@ -201,13 +267,13 @@ The platform abstraction powers the Shovel CLI:
201
267
 
202
268
  ```bash
203
269
  # Auto-detect and run
204
- shovel develop app.js
270
+ shovel develop server.js
205
271
 
206
272
  # Explicit platform targeting
207
- shovel develop app.js --platform=bun --workers=4
273
+ shovel develop server.js --platform=bun --workers=4
208
274
 
209
275
  # Platform-specific builds
210
- shovel build app.js --platform=cloudflare
276
+ shovel build server.js --platform=cloudflare
211
277
  ```
212
278
 
213
279
  ## License
package/package.json CHANGED
@@ -1,19 +1,25 @@
1
1
  {
2
2
  "name": "@b9g/platform",
3
- "version": "0.1.3",
4
- "description": "Base Platform interface and types for Shovel deployment adapters",
3
+ "version": "0.1.5",
4
+ "description": "ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.",
5
5
  "keywords": [
6
- "shovel",
7
- "platform",
6
+ "serviceworker",
7
+ "universal",
8
8
  "deployment",
9
- "adapter",
10
- "interface"
9
+ "platform",
10
+ "registry",
11
+ "node",
12
+ "bun",
13
+ "cloudflare",
14
+ "workers",
15
+ "shovel"
11
16
  ],
12
17
  "dependencies": {
13
- "@types/wicg-file-system-access": "^2023.10.7"
18
+ "@b9g/async-context": "^0.1.1",
19
+ "@logtape/logtape": "^1.2.0"
14
20
  },
15
21
  "devDependencies": {
16
- "@b9g/libuild": "^0.1.10",
22
+ "@b9g/libuild": "^0.1.11",
17
23
  "bun-types": "latest"
18
24
  },
19
25
  "type": "module",
@@ -24,38 +30,38 @@
24
30
  "types": "./src/index.d.ts",
25
31
  "import": "./src/index.js"
26
32
  },
27
- "./types": {
28
- "types": "./src/types.d.ts",
29
- "import": "./src/types.js"
33
+ "./package.json": "./package.json",
34
+ "./worker-pool": {
35
+ "types": "./src/worker-pool.d.ts",
36
+ "import": "./src/worker-pool.js"
30
37
  },
31
- "./types.js": {
32
- "types": "./src/types.d.ts",
33
- "import": "./src/types.js"
38
+ "./worker-pool.js": {
39
+ "types": "./src/worker-pool.d.ts",
40
+ "import": "./src/worker-pool.js"
34
41
  },
35
- "./package.json": "./package.json",
36
- "./adapter-registry": {
37
- "types": "./src/adapter-registry.d.ts",
38
- "import": "./src/adapter-registry.js"
42
+ "./runtime": {
43
+ "types": "./src/runtime.d.ts",
44
+ "import": "./src/runtime.js"
39
45
  },
40
- "./adapter-registry.js": {
41
- "types": "./src/adapter-registry.d.ts",
42
- "import": "./src/adapter-registry.js"
46
+ "./runtime.js": {
47
+ "types": "./src/runtime.d.ts",
48
+ "import": "./src/runtime.js"
43
49
  },
44
- "./base-platform": {
45
- "types": "./src/base-platform.d.ts",
46
- "import": "./src/base-platform.js"
50
+ "./config": {
51
+ "types": "./src/config.d.ts",
52
+ "import": "./src/config.js"
47
53
  },
48
- "./base-platform.js": {
49
- "types": "./src/base-platform.d.ts",
50
- "import": "./src/base-platform.js"
54
+ "./config.js": {
55
+ "types": "./src/config.d.ts",
56
+ "import": "./src/config.js"
51
57
  },
52
- "./filesystem": {
53
- "types": "./src/filesystem.d.ts",
54
- "import": "./src/filesystem.js"
58
+ "./cookie-store": {
59
+ "types": "./src/cookie-store.d.ts",
60
+ "import": "./src/cookie-store.js"
55
61
  },
56
- "./filesystem.js": {
57
- "types": "./src/filesystem.d.ts",
58
- "import": "./src/filesystem.js"
62
+ "./cookie-store.js": {
63
+ "types": "./src/cookie-store.d.ts",
64
+ "import": "./src/cookie-store.js"
59
65
  },
60
66
  "./index": {
61
67
  "types": "./src/index.d.ts",
@@ -65,61 +71,13 @@
65
71
  "types": "./src/index.d.ts",
66
72
  "import": "./src/index.js"
67
73
  },
68
- "./registry": {
69
- "types": "./src/registry.d.ts",
70
- "import": "./src/registry.js"
71
- },
72
- "./registry.js": {
73
- "types": "./src/registry.d.ts",
74
- "import": "./src/registry.js"
75
- },
76
- "./service-worker": {
77
- "types": "./src/service-worker.d.ts",
78
- "import": "./src/service-worker.js"
79
- },
80
- "./service-worker.js": {
81
- "types": "./src/service-worker.d.ts",
82
- "import": "./src/service-worker.js"
83
- },
84
- "./utils": {
85
- "types": "./src/utils.d.ts",
86
- "import": "./src/utils.js"
87
- },
88
- "./utils.js": {
89
- "types": "./src/utils.d.ts",
90
- "import": "./src/utils.js"
91
- },
92
- "./detection": {
93
- "types": "./src/detection.d.ts",
94
- "import": "./src/detection.js"
95
- },
96
- "./detection.js": {
97
- "types": "./src/detection.d.ts",
98
- "import": "./src/detection.js"
99
- },
100
- "./directory-storage": {
101
- "types": "./src/directory-storage.d.ts",
102
- "import": "./src/directory-storage.js"
103
- },
104
- "./directory-storage.js": {
105
- "types": "./src/directory-storage.d.ts",
106
- "import": "./src/directory-storage.js"
107
- },
108
- "./worker-pool": {
109
- "types": "./src/worker-pool.d.ts",
110
- "import": "./src/worker-pool.js"
111
- },
112
- "./worker-pool.js": {
113
- "types": "./src/worker-pool.d.ts",
114
- "import": "./src/worker-pool.js"
115
- },
116
- "./worker-web": {
117
- "types": "./src/worker-web.d.ts",
118
- "import": "./src/worker-web.js"
74
+ "./single-threaded": {
75
+ "types": "./src/single-threaded.d.ts",
76
+ "import": "./src/single-threaded.js"
119
77
  },
120
- "./worker-web.js": {
121
- "types": "./src/worker-web.d.ts",
122
- "import": "./src/worker-web.js"
78
+ "./single-threaded.js": {
79
+ "types": "./src/single-threaded.d.ts",
80
+ "import": "./src/single-threaded.js"
123
81
  }
124
82
  }
125
83
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Configuration expression parser
3
+ *
4
+ * Embeddable JavaScript-like expressions for JSON config:
5
+ * - ALL_CAPS = env var reference (e.g., NODE_ENV, PORT)
6
+ * - Everything else = string literal (kebab-case, URLs, camelCase, PascalCase)
7
+ * - Quoted strings = explicit strings (escape hatch)
8
+ * - JavaScript keywords: true, false, null, undefined
9
+ * - Operators: ||, &&, ===, !==, ==, !=, ? :, !
10
+ * - No eval - uses recursive descent parser
11
+ *
12
+ * Examples:
13
+ * "PORT || 3000"
14
+ * "NODE_ENV === production ? redis : memory"
15
+ * "REDIS_URL || redis://localhost:6379"
16
+ * "S3_BUCKET || my-bucket-name"
17
+ * "BASE_PATH || ./uploads"
18
+ */
19
+ /**
20
+ * Parse a configuration expression with the DSL
21
+ */
22
+ export declare function parseConfigExpr(expr: string, env?: Record<string, string | undefined>, options?: {
23
+ strict?: boolean;
24
+ }): any;
25
+ /**
26
+ * Process a config value (handles nested objects/arrays)
27
+ */
28
+ export declare function processConfigValue(value: any, env?: Record<string, string | undefined>, options?: {
29
+ strict?: boolean;
30
+ }): any;
31
+ /**
32
+ * Match a name against config patterns
33
+ *
34
+ * Priority:
35
+ * 1. Exact match: "sessions" matches "sessions"
36
+ * 2. Prefix patterns: "api-*" matches "api-v1", "api-v2" (longest first)
37
+ * 3. Catch-all: "*" matches everything
38
+ *
39
+ * Examples:
40
+ * matchPattern("sessions", {"sessions": {...}, "*": {...}}) → sessions config
41
+ * matchPattern("api-v1", {"api-*": {...}, "*": {...}}) → api-* config
42
+ * matchPattern("random", {"*": {...}}) → * config
43
+ */
44
+ export declare function matchPattern<T>(name: string, config: Record<string, T>): T | undefined;
45
+ export interface CacheConfig {
46
+ provider?: string | number;
47
+ url?: string | number;
48
+ maxEntries?: string | number;
49
+ TTL?: string | number;
50
+ }
51
+ export interface BucketConfig {
52
+ provider?: string | number;
53
+ path?: string | number;
54
+ bucket?: string | number;
55
+ region?: string | number;
56
+ endpoint?: string | number;
57
+ }
58
+ export interface ShovelConfig {
59
+ port?: number | string;
60
+ host?: string;
61
+ workers?: number | string;
62
+ caches?: Record<string, CacheConfig>;
63
+ buckets?: Record<string, BucketConfig>;
64
+ }
65
+ export interface ProcessedShovelConfig {
66
+ port: number;
67
+ host: string;
68
+ workers: number;
69
+ caches: Record<string, CacheConfig>;
70
+ buckets: Record<string, BucketConfig>;
71
+ }
72
+ /**
73
+ * Load Shovel configuration from shovel.json or package.json
74
+ * Priority: shovel.json > package.json "shovel" field > defaults
75
+ * @param cwd - Current working directory (must be provided by runtime adapter)
76
+ */
77
+ export declare function loadConfig(cwd: string): ProcessedShovelConfig;
78
+ /**
79
+ * Get cache config for a specific cache name (with pattern matching)
80
+ */
81
+ export declare function getCacheConfig(config: ProcessedShovelConfig, name: string): CacheConfig;
82
+ /**
83
+ * Get bucket config for a specific bucket name (with pattern matching)
84
+ */
85
+ export declare function getBucketConfig(config: ProcessedShovelConfig, name: string): BucketConfig;
86
+ export interface BucketFactoryOptions {
87
+ /** Base directory for path resolution (entrypoint directory) - REQUIRED */
88
+ baseDir: string;
89
+ /** Shovel configuration for overrides */
90
+ config?: ProcessedShovelConfig;
91
+ }
92
+ /**
93
+ * Creates a bucket factory function for CustomBucketStorage.
94
+ * Lazily imports bucket implementations.
95
+ */
96
+ export declare function createBucketFactory(options: BucketFactoryOptions): (name: string) => Promise<FileSystemDirectoryHandle>;
97
+ export interface CacheFactoryOptions {
98
+ /** Shovel configuration for cache settings */
99
+ config?: ProcessedShovelConfig;
100
+ }
101
+ /**
102
+ * Creates a cache factory function for CustomCacheStorage.
103
+ * Lazily imports cache implementations.
104
+ */
105
+ export declare function createCacheFactory(options?: CacheFactoryOptions): (name: string) => Promise<Cache>;