@b9g/platform 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,215 @@
1
+ # @b9g/platform
2
+
3
+ Universal platform abstraction for ServiceWorker-style applications with automatic platform detection and worker thread architecture.
4
+
5
+ ## Features
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
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @b9g/platform
17
+ ```
18
+
19
+ ## Platform Packages
20
+
21
+ Install platform-specific implementations:
22
+
23
+ ```bash
24
+ # For Node.js
25
+ npm install @b9g/platform-node
26
+
27
+ # For Bun
28
+ npm install @b9g/platform-bun
29
+
30
+ # For Cloudflare Workers
31
+ npm install @b9g/platform-cloudflare
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```javascript
37
+ import { createPlatform } from '@b9g/platform';
38
+
39
+ // Auto-detect platform
40
+ const platform = await createPlatform('auto');
41
+
42
+ // Load ServiceWorker app
43
+ const serviceWorker = await platform.loadServiceWorker('./app.js', {
44
+ workerCount: 2,
45
+ hotReload: true
46
+ });
47
+
48
+ // Create server
49
+ const server = platform.createServer(serviceWorker.handleRequest, {
50
+ port: 3000,
51
+ host: 'localhost'
52
+ });
53
+
54
+ await server.listen();
55
+ ```
56
+
57
+ ## ServiceWorker Pattern
58
+
59
+ Write your app as a ServiceWorker entrypoint:
60
+
61
+ ```javascript
62
+ // app.js - ServiceWorker-style entrypoint
63
+ import { Router } from '@b9g/router';
64
+
65
+ const router = new Router();
66
+ router.get('/', () => new Response('Hello World!'));
67
+
68
+ // ServiceWorker lifecycle events
69
+ addEventListener('install', event => {
70
+ console.log('App installing...');
71
+ });
72
+
73
+ addEventListener('activate', event => {
74
+ console.log('App activated!');
75
+ });
76
+
77
+ // Handle fetch events
78
+ addEventListener('fetch', event => {
79
+ event.respondWith(router.handler(event.request));
80
+ });
81
+ ```
82
+
83
+ ## Platform Detection
84
+
85
+ ```javascript
86
+ import {
87
+ detectPlatform,
88
+ createPlatform,
89
+ displayPlatformInfo
90
+ } from '@b9g/platform';
91
+
92
+ // Detect current runtime
93
+ const detected = detectPlatform();
94
+ console.log(detected); // { runtime: 'bun', platforms: ['bun', 'node'] }
95
+
96
+ // Create platform instance
97
+ const platform = await createPlatform('bun', {
98
+ // Platform-specific options
99
+ });
100
+
101
+ // Display platform information
102
+ displayPlatformInfo(detected);
103
+ ```
104
+
105
+ ## Worker Architecture
106
+
107
+ ```javascript
108
+ const platform = await createPlatform('node');
109
+
110
+ const serviceWorker = await platform.loadServiceWorker('./app.js', {
111
+ workerCount: 4, // Number of worker threads
112
+ hotReload: true, // Enable hot reloading
113
+ caches: {
114
+ pages: { type: 'memory', maxEntries: 1000 },
115
+ api: { type: 'memory', ttl: 300 }
116
+ }
117
+ });
118
+
119
+ // Workers coordinate through PostMessage
120
+ // Each worker loads your ServiceWorker app
121
+ // Cache operations are coordinated across workers
122
+ ```
123
+
124
+ ## Platform-Specific Features
125
+
126
+ ### Node.js Platform
127
+
128
+ ```javascript
129
+ import { createNodePlatform } from '@b9g/platform-node';
130
+
131
+ const platform = await createNodePlatform({
132
+ // Node.js specific options
133
+ });
134
+ ```
135
+
136
+ ### Bun Platform
137
+
138
+ ```javascript
139
+ import { createBunPlatform } from '@b9g/platform-bun';
140
+
141
+ const platform = await createBunPlatform({
142
+ // Bun specific options
143
+ });
144
+ ```
145
+
146
+ ### Cloudflare Workers Platform
147
+
148
+ ```javascript
149
+ import { createCloudflarePlatform } from '@b9g/platform-cloudflare';
150
+
151
+ const platform = await createCloudflarePlatform({
152
+ // Cloudflare specific options
153
+ });
154
+ ```
155
+
156
+ ## API Reference
157
+
158
+ ### Platform Interface
159
+
160
+ ```typescript
161
+ interface Platform {
162
+ loadServiceWorker(entrypoint: string, options: ServiceWorkerOptions): Promise<ServiceWorkerInstance>;
163
+ createServer(handler: Handler, options: ServerOptions): Server;
164
+ dispose(): Promise<void>;
165
+ }
166
+ ```
167
+
168
+ ### ServiceWorker Options
169
+
170
+ ```typescript
171
+ interface ServiceWorkerOptions {
172
+ workerCount?: number;
173
+ hotReload?: boolean;
174
+ caches?: CacheConfig;
175
+ }
176
+ ```
177
+
178
+ ### Platform Detection
179
+
180
+ ```typescript
181
+ function detectPlatform(): PlatformDetection;
182
+ function createPlatform(platformName: string, options?: any): Promise<Platform>;
183
+ function displayPlatformInfo(detection: PlatformDetection): void;
184
+ ```
185
+
186
+ ## Development vs Production
187
+
188
+ ### Development (2 workers default)
189
+ - Encourages concurrency thinking from the start
190
+ - Hot reloading with VM module isolation
191
+ - Verbose logging and error reporting
192
+
193
+ ### Production (CPU count workers)
194
+ - Maximum throughput with worker-per-core
195
+ - Optimized cache coordination
196
+ - Minimal logging overhead
197
+
198
+ ## Integration with CLI
199
+
200
+ The platform abstraction powers the Shovel CLI:
201
+
202
+ ```bash
203
+ # Auto-detect and run
204
+ shovel develop app.js
205
+
206
+ # Explicit platform targeting
207
+ shovel develop app.js --platform=bun --workers=4
208
+
209
+ # Platform-specific builds
210
+ shovel build app.js --platform=cloudflare
211
+ ```
212
+
213
+ ## License
214
+
215
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/platform",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Base Platform interface and types for Shovel deployment adapters",
5
5
  "keywords": [
6
6
  "shovel",
@@ -12,10 +12,9 @@ export declare abstract class BasePlatform implements Platform {
12
12
  protected config: PlatformConfig;
13
13
  constructor(config?: PlatformConfig);
14
14
  abstract readonly name: string;
15
- abstract readonly distDir: FileSystemDirectoryHandle;
16
15
  abstract loadServiceWorker(entrypoint: string, options?: any): Promise<any>;
17
16
  abstract createServer(handler: any, options?: any): any;
18
- abstract getFileSystemRoot(bucketName: string): Promise<FileSystemDirectoryHandle>;
17
+ abstract getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
19
18
  /**
20
19
  * Create cache storage with dynamic adapter loading
21
20
  * Uses platform defaults when specific cache types aren't configured
@@ -1,41 +1,37 @@
1
1
  /**
2
- * Directory Storage implementation for ServiceWorker self.dirs API
2
+ * Bucket Storage implementation for ServiceWorker self.buckets API
3
3
  *
4
4
  * This implements the proposed web standard interface that parallels CacheStorage
5
5
  * for structured filesystem access in ServiceWorkers.
6
6
  */
7
- import type { DirectoryStorage } from "./service-worker.js";
7
+ import type { BucketStorage as BucketStorageInterface } from "./service-worker.js";
8
8
  /**
9
- * Platform-agnostic directory storage implementation
10
- * Uses a single root directory with well-known subdirectories
9
+ * Platform-agnostic bucket storage implementation
10
+ * Uses bucket pattern where each bucket name maps to a separate filesystem root
11
11
  */
12
- export declare class PlatformDirectoryStorage implements DirectoryStorage {
13
- private rootDir;
14
- private cache;
15
- constructor(rootDir: FileSystemDirectoryHandle);
12
+ export declare class PlatformBucketStorage implements BucketStorageInterface {
13
+ private buckets;
14
+ constructor(rootPath?: string);
16
15
  /**
17
- * Open a named directory - creates if it doesn't exist
18
- * Well-known names: 'assets', 'static', 'server', 'client'
16
+ * Open a named bucket - creates if it doesn't exist
17
+ * Well-known names: 'assets', 'static', 'uploads', 'temp'
18
+ * Special values: '', '/', '.' return the root bucket
19
19
  */
20
20
  open(name: string): Promise<FileSystemDirectoryHandle>;
21
21
  /**
22
- * Check if a named directory exists
22
+ * Check if a named bucket exists
23
23
  */
24
24
  has(name: string): Promise<boolean>;
25
25
  /**
26
- * Delete a named directory and all its contents
26
+ * Delete a named bucket and all its contents
27
27
  */
28
28
  delete(name: string): Promise<boolean>;
29
29
  /**
30
- * List all available directory names
30
+ * List all available bucket names
31
31
  */
32
32
  keys(): Promise<string[]>;
33
- /**
34
- * Clear the cache (useful for hot reloading)
35
- */
36
- clearCache(): void;
37
33
  }
38
34
  /**
39
- * Create a DirectoryStorage instance from a root directory
35
+ * Create a BucketStorage instance from a root path
40
36
  */
41
- export declare function createDirectoryStorage(rootDir: FileSystemDirectoryHandle): DirectoryStorage;
37
+ export declare function createBucketStorage(rootPath?: string): BucketStorageInterface;
@@ -1,78 +1,47 @@
1
1
  /// <reference types="./directory-storage.d.ts" />
2
2
  // src/directory-storage.ts
3
- var PlatformDirectoryStorage = class {
4
- rootDir;
5
- cache = /* @__PURE__ */ new Map();
6
- constructor(rootDir) {
7
- this.rootDir = rootDir;
3
+ import { BucketStorage, LocalBucket } from "@b9g/filesystem";
4
+ var PlatformBucketStorage = class {
5
+ buckets;
6
+ constructor(rootPath = "./dist") {
7
+ this.buckets = new BucketStorage((name) => {
8
+ if (name === "" || name === "/" || name === ".") {
9
+ return new LocalBucket(rootPath);
10
+ }
11
+ return new LocalBucket(`${rootPath}/${name}`);
12
+ });
8
13
  }
9
14
  /**
10
- * Open a named directory - creates if it doesn't exist
11
- * Well-known names: 'assets', 'static', 'server', 'client'
15
+ * Open a named bucket - creates if it doesn't exist
16
+ * Well-known names: 'assets', 'static', 'uploads', 'temp'
17
+ * Special values: '', '/', '.' return the root bucket
12
18
  */
13
19
  async open(name) {
14
- if (this.cache.has(name)) {
15
- return this.cache.get(name);
16
- }
17
- try {
18
- const dirHandle = await this.rootDir.getDirectoryHandle(name);
19
- this.cache.set(name, dirHandle);
20
- return dirHandle;
21
- } catch (error) {
22
- const dirHandle = await this.rootDir.getDirectoryHandle(name, { create: true });
23
- this.cache.set(name, dirHandle);
24
- return dirHandle;
25
- }
20
+ return await this.buckets.open(name);
26
21
  }
27
22
  /**
28
- * Check if a named directory exists
23
+ * Check if a named bucket exists
29
24
  */
30
25
  async has(name) {
31
- try {
32
- await this.rootDir.getDirectoryHandle(name);
33
- return true;
34
- } catch {
35
- return false;
36
- }
26
+ return await this.buckets.has(name);
37
27
  }
38
28
  /**
39
- * Delete a named directory and all its contents
29
+ * Delete a named bucket and all its contents
40
30
  */
41
31
  async delete(name) {
42
- try {
43
- await this.rootDir.removeEntry(name, { recursive: true });
44
- this.cache.delete(name);
45
- return true;
46
- } catch {
47
- return false;
48
- }
32
+ return await this.buckets.delete(name);
49
33
  }
50
34
  /**
51
- * List all available directory names
35
+ * List all available bucket names
52
36
  */
53
37
  async keys() {
54
- const keys = [];
55
- try {
56
- for await (const [name, handle] of this.rootDir.entries()) {
57
- if (handle.kind === "directory") {
58
- keys.push(name);
59
- }
60
- }
61
- } catch {
62
- }
63
- return keys.sort();
64
- }
65
- /**
66
- * Clear the cache (useful for hot reloading)
67
- */
68
- clearCache() {
69
- this.cache.clear();
38
+ return await this.buckets.keys();
70
39
  }
71
40
  };
72
- function createDirectoryStorage(rootDir) {
73
- return new PlatformDirectoryStorage(rootDir);
41
+ function createBucketStorage(rootPath = "./dist") {
42
+ return new PlatformBucketStorage(rootPath);
74
43
  }
75
44
  export {
76
- PlatformDirectoryStorage,
77
- createDirectoryStorage
45
+ PlatformBucketStorage,
46
+ createBucketStorage
78
47
  };
@@ -2,7 +2,15 @@
2
2
  * File System Access API implementation
3
3
  */
4
4
  /**
5
- * Get the file system root handle for the specified name
5
+ * Get the file system directory handle for the specified name
6
6
  * Auto-registers Node.js platform if no platform is detected
7
7
  */
8
+ export declare function getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
9
+ /**
10
+ * @deprecated Use getDirectoryHandle() instead
11
+ */
12
+ export declare function getBucket(name?: string): Promise<FileSystemDirectoryHandle>;
13
+ /**
14
+ * @deprecated Use getDirectoryHandle() instead
15
+ */
8
16
  export declare function getFileSystemRoot(name?: string): Promise<FileSystemDirectoryHandle>;
package/src/filesystem.js CHANGED
@@ -1,10 +1,20 @@
1
1
  /// <reference types="./filesystem.d.ts" />
2
2
  // src/filesystem.ts
3
3
  import { getPlatformAsync } from "./registry.js";
4
+ async function getDirectoryHandle(name) {
5
+ const platform = await getPlatformAsync();
6
+ return await platform.getDirectoryHandle(name);
7
+ }
8
+ async function getBucket(name) {
9
+ const platform = await getPlatformAsync();
10
+ return await platform.getDirectoryHandle(name || "");
11
+ }
4
12
  async function getFileSystemRoot(name) {
5
13
  const platform = await getPlatformAsync();
6
- return await platform.getFileSystemRoot(name);
14
+ return await platform.getDirectoryHandle(name || "");
7
15
  }
8
16
  export {
17
+ getBucket,
18
+ getDirectoryHandle,
9
19
  getFileSystemRoot
10
20
  };
package/src/index.d.ts CHANGED
@@ -6,9 +6,9 @@
6
6
  */
7
7
  export type { Platform, CacheConfig, CacheBackendConfig, ServerOptions, CorsConfig, Handler, Server, ServiceWorkerOptions, ServiceWorkerInstance, PlatformDetection, PlatformRegistry, } from "./types.js";
8
8
  export { BasePlatform } from "./types.js";
9
- export { ServiceWorkerRuntime, createServiceWorkerGlobals, type ShovelFetchEvent, type ShovelInstallEvent, type ShovelActivateEvent, type ShovelStaticEvent, type DirectoryStorage, } from "./service-worker.js";
10
- export { PlatformDirectoryStorage, createDirectoryStorage, } from "./directory-storage.js";
9
+ export { ServiceWorkerRuntime, createServiceWorkerGlobals, type ShovelFetchEvent, type ShovelInstallEvent, type ShovelActivateEvent, type ShovelStaticEvent, type BucketStorage as BucketStorageInterface, } from "./service-worker.js";
10
+ export { PlatformBucketStorage, createBucketStorage, } from "./directory-storage.js";
11
11
  export { platformRegistry, detectPlatform, getPlatform, getPlatformAsync, } from "./registry.js";
12
12
  export { detectRuntime, detectDevelopmentPlatform, detectPlatforms, getBestPlatformDetection, resolvePlatform, createPlatform, displayPlatformInfo, } from "./detection.js";
13
13
  export { parseTTL, mergeCacheConfig, validateCacheConfig, createCorsHeaders, mergeHeaders, isPreflightRequest, createPreflightResponse, } from "./utils.js";
14
- export { getFileSystemRoot } from "./filesystem.js";
14
+ export { getDirectoryHandle, getBucket, getFileSystemRoot } from "./filesystem.js";
package/src/index.js CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  createServiceWorkerGlobals
7
7
  } from "./service-worker.js";
8
8
  import {
9
- PlatformDirectoryStorage,
10
- createDirectoryStorage
9
+ PlatformBucketStorage,
10
+ createBucketStorage
11
11
  } from "./directory-storage.js";
12
12
  import {
13
13
  platformRegistry,
@@ -33,13 +33,13 @@ import {
33
33
  isPreflightRequest,
34
34
  createPreflightResponse
35
35
  } from "./utils.js";
36
- import { getFileSystemRoot } from "./filesystem.js";
36
+ import { getDirectoryHandle, getBucket, getFileSystemRoot } from "./filesystem.js";
37
37
  export {
38
38
  BasePlatform,
39
- PlatformDirectoryStorage,
39
+ PlatformBucketStorage,
40
40
  ServiceWorkerRuntime,
41
+ createBucketStorage,
41
42
  createCorsHeaders,
42
- createDirectoryStorage,
43
43
  createPlatform,
44
44
  createPreflightResponse,
45
45
  createServiceWorkerGlobals,
@@ -49,6 +49,8 @@ export {
49
49
  detectRuntime,
50
50
  displayPlatformInfo,
51
51
  getBestPlatformDetection,
52
+ getBucket,
53
+ getDirectoryHandle,
52
54
  getFileSystemRoot,
53
55
  getPlatform,
54
56
  getPlatformAsync,
@@ -75,25 +75,25 @@ export declare class ServiceWorkerRuntime extends EventTarget {
75
75
  reset(): void;
76
76
  }
77
77
  /**
78
- * Directory storage interface - parallels CacheStorage for filesystem access
78
+ * Bucket storage interface - parallels CacheStorage for filesystem access
79
79
  * This could become a future web standard
80
80
  */
81
- export interface DirectoryStorage {
81
+ export interface BucketStorage {
82
82
  /**
83
- * Open a named directory - returns FileSystemDirectoryHandle
84
- * Well-known names: 'assets', 'static', 'server', 'client'
83
+ * Open a named bucket - returns FileSystemDirectoryHandle (root of that bucket)
84
+ * Well-known names: 'assets', 'static', 'uploads', 'temp'
85
85
  */
86
86
  open(name: string): Promise<FileSystemDirectoryHandle>;
87
87
  /**
88
- * Check if a named directory exists
88
+ * Check if a named bucket exists
89
89
  */
90
90
  has(name: string): Promise<boolean>;
91
91
  /**
92
- * Delete a named directory and all its contents
92
+ * Delete a named bucket and all its contents
93
93
  */
94
94
  delete(name: string): Promise<boolean>;
95
95
  /**
96
- * List all available directory names
96
+ * List all available bucket names
97
97
  */
98
98
  keys(): Promise<string[]>;
99
99
  }
@@ -102,7 +102,7 @@ export interface DirectoryStorage {
102
102
  */
103
103
  export declare function createServiceWorkerGlobals(runtime: ServiceWorkerRuntime, options?: {
104
104
  caches?: any;
105
- dirs?: DirectoryStorage;
105
+ buckets?: BucketStorage;
106
106
  }): {
107
107
  self: ServiceWorkerRuntime;
108
108
  addEventListener: any;
@@ -149,8 +149,8 @@ function createServiceWorkerGlobals(runtime, options = {}) {
149
149
  if (options.caches) {
150
150
  runtime.caches = options.caches;
151
151
  }
152
- if (options.dirs) {
153
- runtime.dirs = options.dirs;
152
+ if (options.buckets) {
153
+ runtime.buckets = options.buckets;
154
154
  }
155
155
  return {
156
156
  self: runtime,
package/src/types.d.ts CHANGED
@@ -87,6 +87,8 @@ export interface FilesystemConfig {
87
87
  secretAccessKey?: string;
88
88
  token?: string;
89
89
  };
90
+ /** Factory function for creating directory storage */
91
+ factory?: any;
90
92
  /** Additional adapter-specific options */
91
93
  [key: string]: any;
92
94
  }
@@ -166,13 +168,18 @@ export interface ServiceWorkerInstance {
166
168
  */
167
169
  export interface Server {
168
170
  /** Start listening for requests */
169
- listen(): Promise<void> | void;
171
+ listen(): Promise<void>;
170
172
  /** Stop the server */
171
- close(): Promise<void> | void;
173
+ close(): Promise<void>;
174
+ /** Get server address information */
175
+ address(): {
176
+ port: number;
177
+ host: string;
178
+ };
172
179
  /** Get server URL */
173
- url?: string;
174
- /** Platform-specific server instance */
175
- instance?: any;
180
+ readonly url: string;
181
+ /** Whether server is ready to accept requests */
182
+ readonly ready: boolean;
176
183
  }
177
184
  /**
178
185
  * Platform interface - ServiceWorker entrypoint loader for JavaScript runtimes
@@ -184,11 +191,6 @@ export interface Platform {
184
191
  * Platform name for identification
185
192
  */
186
193
  readonly name: string;
187
- /**
188
- * Build artifacts filesystem (install-time only)
189
- * Available during install handlers to copy built files to runtime storage
190
- */
191
- readonly distDir: FileSystemDirectoryHandle;
192
194
  /**
193
195
  * THE MAIN JOB - Load and run a ServiceWorker-style entrypoint
194
196
  * This is where all the platform-specific complexity lives
@@ -202,16 +204,21 @@ export interface Platform {
202
204
  * - Bun: filesystem with optimized writes
203
205
  */
204
206
  createCaches(config?: CacheConfig): Promise<CacheStorage>;
207
+ /**
208
+ * SUPPORTING UTILITY - Create bucket storage with platform-optimized backends
209
+ * Uses factory pattern to route bucket names to different filesystem adapters
210
+ */
211
+ createBuckets(config?: FilesystemConfig): Promise<any>;
205
212
  /**
206
213
  * SUPPORTING UTILITY - Create server instance for this platform
207
214
  */
208
215
  createServer(handler: Handler, options?: ServerOptions): Server;
209
216
  /**
210
- * SUPPORTING UTILITY - Get filesystem root for bucket/container name
217
+ * SUPPORTING UTILITY - Get filesystem directory handle
211
218
  * Maps directly to cloud storage buckets (S3, R2) or local directories
212
- * @param bucketName - The bucket/container/directory name
219
+ * @param name - Directory name. Use "" for root directory
213
220
  */
214
- getFileSystemRoot(bucketName: string): Promise<FileSystemDirectoryHandle>;
221
+ getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
215
222
  }
216
223
  /**
217
224
  * Platform detection result