@b9g/platform 0.1.1 → 0.1.3
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 +215 -0
- package/package.json +17 -1
- package/src/base-platform.d.ts +1 -2
- package/src/directory-storage.d.ts +17 -17
- package/src/directory-storage.js +27 -52
- package/src/filesystem.d.ts +9 -1
- package/src/filesystem.js +11 -1
- package/src/index.d.ts +4 -3
- package/src/index.js +9 -5
- package/src/service-worker.d.ts +64 -38
- package/src/service-worker.js +146 -84
- package/src/types.d.ts +20 -13
- package/src/worker-pool.d.ts +103 -0
- package/src/worker-pool.js +271 -0
- package/src/worker-web.js +119 -0
package/src/service-worker.d.ts
CHANGED
|
@@ -3,39 +3,56 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides ServiceWorker APIs (self, addEventListener, etc.) in any JavaScript runtime
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* ExtendableEvent base class following ServiceWorker spec
|
|
8
|
+
*/
|
|
9
|
+
export declare class ExtendableEvent extends Event {
|
|
10
|
+
private promises;
|
|
11
|
+
private pendingPromises;
|
|
12
|
+
constructor(type: string, pendingPromises: Set<Promise<any>>);
|
|
13
|
+
waitUntil(promise: Promise<any>): void;
|
|
14
|
+
getPromises(): Promise<any>[];
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* ServiceWorker-style fetch event
|
|
8
18
|
*/
|
|
9
|
-
export
|
|
10
|
-
readonly type: "fetch";
|
|
19
|
+
export declare class FetchEvent extends ExtendableEvent {
|
|
11
20
|
readonly request: Request;
|
|
21
|
+
private responsePromise;
|
|
22
|
+
private responded;
|
|
23
|
+
constructor(request: Request, pendingPromises: Set<Promise<any>>);
|
|
12
24
|
respondWith(response: Response | Promise<Response>): void;
|
|
13
|
-
|
|
25
|
+
getResponse(): Promise<Response> | null;
|
|
26
|
+
hasResponded(): boolean;
|
|
14
27
|
}
|
|
15
28
|
/**
|
|
16
29
|
* ServiceWorker-style install event
|
|
17
30
|
*/
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
waitUntil(promise: Promise<any>): void;
|
|
31
|
+
export declare class InstallEvent extends ExtendableEvent {
|
|
32
|
+
constructor(pendingPromises: Set<Promise<any>>);
|
|
21
33
|
}
|
|
22
34
|
/**
|
|
23
35
|
* ServiceWorker-style activate event
|
|
24
36
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
waitUntil(promise: Promise<any>): void;
|
|
37
|
+
export declare class ActivateEvent extends ExtendableEvent {
|
|
38
|
+
constructor(pendingPromises: Set<Promise<any>>);
|
|
28
39
|
}
|
|
29
40
|
/**
|
|
30
|
-
*
|
|
41
|
+
* Legacy interfaces for backward compatibility
|
|
31
42
|
*/
|
|
32
|
-
export interface
|
|
33
|
-
readonly type: "
|
|
34
|
-
readonly
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
export interface ShovelFetchEvent extends Event {
|
|
44
|
+
readonly type: "fetch";
|
|
45
|
+
readonly request: Request;
|
|
46
|
+
respondWith(response: Response | Promise<Response>): void;
|
|
47
|
+
waitUntil(promise: Promise<any>): void;
|
|
48
|
+
}
|
|
49
|
+
export interface ShovelInstallEvent extends Event {
|
|
50
|
+
readonly type: "install";
|
|
51
|
+
waitUntil(promise: Promise<any>): void;
|
|
52
|
+
}
|
|
53
|
+
export interface ShovelActivateEvent extends Event {
|
|
54
|
+
readonly type: "activate";
|
|
55
|
+
waitUntil(promise: Promise<any>): void;
|
|
39
56
|
}
|
|
40
57
|
/**
|
|
41
58
|
* ServiceWorker runtime that can be embedded in any platform
|
|
@@ -44,7 +61,10 @@ export declare class ServiceWorkerRuntime extends EventTarget {
|
|
|
44
61
|
private pendingPromises;
|
|
45
62
|
private isInstalled;
|
|
46
63
|
private isActivated;
|
|
64
|
+
private eventListeners;
|
|
47
65
|
constructor();
|
|
66
|
+
addEventListener(type: string, listener: Function): void;
|
|
67
|
+
removeEventListener(type: string, listener: Function): void;
|
|
48
68
|
/**
|
|
49
69
|
* Create a fetch event and dispatch it
|
|
50
70
|
*/
|
|
@@ -57,10 +77,6 @@ export declare class ServiceWorkerRuntime extends EventTarget {
|
|
|
57
77
|
* Activate the ServiceWorker
|
|
58
78
|
*/
|
|
59
79
|
activate(): Promise<void>;
|
|
60
|
-
/**
|
|
61
|
-
* Collect static routes for pre-rendering
|
|
62
|
-
*/
|
|
63
|
-
collectStaticRoutes(outDir: string, baseUrl?: string): Promise<string[]>;
|
|
64
80
|
/**
|
|
65
81
|
* Check if ready to handle requests
|
|
66
82
|
*/
|
|
@@ -75,25 +91,29 @@ export declare class ServiceWorkerRuntime extends EventTarget {
|
|
|
75
91
|
reset(): void;
|
|
76
92
|
}
|
|
77
93
|
/**
|
|
78
|
-
*
|
|
94
|
+
* Bucket storage interface - parallels CacheStorage for filesystem access
|
|
79
95
|
* This could become a future web standard
|
|
80
96
|
*/
|
|
81
|
-
export interface
|
|
97
|
+
export interface BucketStorage {
|
|
82
98
|
/**
|
|
83
|
-
* Open a named
|
|
84
|
-
* Well-known names: 'assets', 'static', '
|
|
99
|
+
* Open a named bucket - returns FileSystemDirectoryHandle (root of that bucket)
|
|
100
|
+
* Well-known names: 'assets', 'static', 'uploads', 'temp'
|
|
85
101
|
*/
|
|
86
102
|
open(name: string): Promise<FileSystemDirectoryHandle>;
|
|
87
103
|
/**
|
|
88
|
-
*
|
|
104
|
+
* Alias for open() - for compatibility with File System Access API naming
|
|
105
|
+
*/
|
|
106
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
107
|
+
/**
|
|
108
|
+
* Check if a named bucket exists
|
|
89
109
|
*/
|
|
90
110
|
has(name: string): Promise<boolean>;
|
|
91
111
|
/**
|
|
92
|
-
* Delete a named
|
|
112
|
+
* Delete a named bucket and all its contents
|
|
93
113
|
*/
|
|
94
114
|
delete(name: string): Promise<boolean>;
|
|
95
115
|
/**
|
|
96
|
-
* List all available
|
|
116
|
+
* List all available bucket names
|
|
97
117
|
*/
|
|
98
118
|
keys(): Promise<string[]>;
|
|
99
119
|
}
|
|
@@ -102,17 +122,10 @@ export interface DirectoryStorage {
|
|
|
102
122
|
*/
|
|
103
123
|
export declare function createServiceWorkerGlobals(runtime: ServiceWorkerRuntime, options?: {
|
|
104
124
|
caches?: any;
|
|
105
|
-
|
|
125
|
+
buckets?: BucketStorage;
|
|
126
|
+
isDevelopment?: boolean;
|
|
127
|
+
hotReload?: () => Promise<void>;
|
|
106
128
|
}): {
|
|
107
|
-
self: ServiceWorkerRuntime;
|
|
108
|
-
addEventListener: any;
|
|
109
|
-
removeEventListener: any;
|
|
110
|
-
dispatchEvent: any;
|
|
111
|
-
skipWaiting: () => Promise<void>;
|
|
112
|
-
clients: {
|
|
113
|
-
claim: () => Promise<void>;
|
|
114
|
-
matchAll: () => Promise<any[]>;
|
|
115
|
-
};
|
|
116
129
|
console: Console;
|
|
117
130
|
setTimeout: typeof setTimeout;
|
|
118
131
|
clearTimeout: typeof clearTimeout;
|
|
@@ -146,4 +159,17 @@ export declare function createServiceWorkerGlobals(runtime: ServiceWorkerRuntime
|
|
|
146
159
|
new (init?: string[][] | Record<string, string> | string | URLSearchParams): URLSearchParams;
|
|
147
160
|
prototype: URLSearchParams;
|
|
148
161
|
};
|
|
162
|
+
caches: any;
|
|
163
|
+
buckets: BucketStorage;
|
|
164
|
+
self: ServiceWorkerRuntime;
|
|
165
|
+
addEventListener: any;
|
|
166
|
+
removeEventListener: any;
|
|
167
|
+
dispatchEvent: any;
|
|
168
|
+
skipWaiting: () => Promise<void>;
|
|
169
|
+
clients: {
|
|
170
|
+
claim(): Promise<void>;
|
|
171
|
+
get(id: string): Promise<any>;
|
|
172
|
+
matchAll(options?: any): Promise<any[]>;
|
|
173
|
+
openWindow(url: string | URL): Promise<any>;
|
|
174
|
+
};
|
|
149
175
|
};
|
package/src/service-worker.js
CHANGED
|
@@ -1,12 +1,81 @@
|
|
|
1
1
|
/// <reference types="./service-worker.d.ts" />
|
|
2
2
|
// src/service-worker.ts
|
|
3
|
+
var ExtendableEvent = class extends Event {
|
|
4
|
+
promises = [];
|
|
5
|
+
pendingPromises;
|
|
6
|
+
constructor(type, pendingPromises) {
|
|
7
|
+
super(type);
|
|
8
|
+
this.pendingPromises = pendingPromises;
|
|
9
|
+
}
|
|
10
|
+
waitUntil(promise) {
|
|
11
|
+
this.promises.push(promise);
|
|
12
|
+
this.pendingPromises.add(promise);
|
|
13
|
+
promise.finally(() => this.pendingPromises.delete(promise));
|
|
14
|
+
}
|
|
15
|
+
getPromises() {
|
|
16
|
+
return [...this.promises];
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var FetchEvent = class extends ExtendableEvent {
|
|
20
|
+
request;
|
|
21
|
+
responsePromise = null;
|
|
22
|
+
responded = false;
|
|
23
|
+
constructor(request, pendingPromises) {
|
|
24
|
+
super("fetch", pendingPromises);
|
|
25
|
+
this.request = request;
|
|
26
|
+
}
|
|
27
|
+
respondWith(response) {
|
|
28
|
+
if (this.responded) {
|
|
29
|
+
throw new Error("respondWith() already called");
|
|
30
|
+
}
|
|
31
|
+
this.responded = true;
|
|
32
|
+
this.responsePromise = Promise.resolve(response);
|
|
33
|
+
}
|
|
34
|
+
getResponse() {
|
|
35
|
+
return this.responsePromise;
|
|
36
|
+
}
|
|
37
|
+
hasResponded() {
|
|
38
|
+
return this.responded;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var InstallEvent = class extends ExtendableEvent {
|
|
42
|
+
constructor(pendingPromises) {
|
|
43
|
+
super("install", pendingPromises);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var ActivateEvent = class extends ExtendableEvent {
|
|
47
|
+
constructor(pendingPromises) {
|
|
48
|
+
super("activate", pendingPromises);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
3
51
|
var ServiceWorkerRuntime = class extends EventTarget {
|
|
4
52
|
pendingPromises = /* @__PURE__ */ new Set();
|
|
5
53
|
isInstalled = false;
|
|
6
54
|
isActivated = false;
|
|
55
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
7
56
|
constructor() {
|
|
8
57
|
super();
|
|
9
58
|
}
|
|
59
|
+
addEventListener(type, listener) {
|
|
60
|
+
super.addEventListener(type, listener);
|
|
61
|
+
if (!this.eventListeners.has(type)) {
|
|
62
|
+
this.eventListeners.set(type, []);
|
|
63
|
+
}
|
|
64
|
+
this.eventListeners.get(type).push(listener);
|
|
65
|
+
}
|
|
66
|
+
removeEventListener(type, listener) {
|
|
67
|
+
super.removeEventListener(type, listener);
|
|
68
|
+
if (this.eventListeners.has(type)) {
|
|
69
|
+
const listeners = this.eventListeners.get(type);
|
|
70
|
+
const index = listeners.indexOf(listener);
|
|
71
|
+
if (index > -1) {
|
|
72
|
+
listeners.splice(index, 1);
|
|
73
|
+
if (listeners.length === 0) {
|
|
74
|
+
this.eventListeners.delete(type);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
10
79
|
/**
|
|
11
80
|
* Create a fetch event and dispatch it
|
|
12
81
|
*/
|
|
@@ -15,28 +84,22 @@ var ServiceWorkerRuntime = class extends EventTarget {
|
|
|
15
84
|
throw new Error("ServiceWorker not activated");
|
|
16
85
|
}
|
|
17
86
|
return new Promise((resolve, reject) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
throw new Error("respondWith() already called");
|
|
25
|
-
}
|
|
26
|
-
responded = true;
|
|
27
|
-
Promise.resolve(response).then(resolve).catch(reject);
|
|
28
|
-
},
|
|
29
|
-
waitUntil: (promise) => {
|
|
30
|
-
promises.push(promise);
|
|
31
|
-
this.pendingPromises.add(promise);
|
|
32
|
-
promise.finally(() => this.pendingPromises.delete(promise));
|
|
87
|
+
const event = new FetchEvent(request, this.pendingPromises);
|
|
88
|
+
process.nextTick(() => {
|
|
89
|
+
this.dispatchEvent(event);
|
|
90
|
+
const promises = event.getPromises();
|
|
91
|
+
if (promises.length > 0) {
|
|
92
|
+
Promise.allSettled(promises).catch(console.error);
|
|
33
93
|
}
|
|
34
94
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
if (event.hasResponded()) {
|
|
97
|
+
const responsePromise = event.getResponse();
|
|
98
|
+
responsePromise.then(resolve).catch(reject);
|
|
99
|
+
} else {
|
|
100
|
+
reject(new Error("No response provided for fetch event"));
|
|
101
|
+
}
|
|
102
|
+
}, 0);
|
|
40
103
|
});
|
|
41
104
|
}
|
|
42
105
|
/**
|
|
@@ -46,22 +109,20 @@ var ServiceWorkerRuntime = class extends EventTarget {
|
|
|
46
109
|
if (this.isInstalled)
|
|
47
110
|
return;
|
|
48
111
|
return new Promise((resolve, reject) => {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.pendingPromises.add(promise);
|
|
55
|
-
promise.finally(() => this.pendingPromises.delete(promise));
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
this.dispatchEvent(event);
|
|
59
|
-
Promise.allSettled(promises).then(() => {
|
|
60
|
-
if (!installCancelled) {
|
|
112
|
+
const event = new InstallEvent(this.pendingPromises);
|
|
113
|
+
process.nextTick(() => {
|
|
114
|
+
this.dispatchEvent(event);
|
|
115
|
+
const promises = event.getPromises();
|
|
116
|
+
if (promises.length === 0) {
|
|
61
117
|
this.isInstalled = true;
|
|
62
118
|
resolve();
|
|
119
|
+
} else {
|
|
120
|
+
Promise.all(promises).then(() => {
|
|
121
|
+
this.isInstalled = true;
|
|
122
|
+
resolve();
|
|
123
|
+
}).catch(reject);
|
|
63
124
|
}
|
|
64
|
-
})
|
|
125
|
+
});
|
|
65
126
|
});
|
|
66
127
|
}
|
|
67
128
|
/**
|
|
@@ -74,46 +135,20 @@ var ServiceWorkerRuntime = class extends EventTarget {
|
|
|
74
135
|
if (this.isActivated)
|
|
75
136
|
return;
|
|
76
137
|
return new Promise((resolve, reject) => {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}).catch(reject);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Collect static routes for pre-rendering
|
|
94
|
-
*/
|
|
95
|
-
async collectStaticRoutes(outDir, baseUrl) {
|
|
96
|
-
return new Promise((resolve, reject) => {
|
|
97
|
-
let routes = [];
|
|
98
|
-
const promises = [];
|
|
99
|
-
const event = Object.assign(new Event("static"), {
|
|
100
|
-
detail: { outDir, baseUrl },
|
|
101
|
-
waitUntil: (promise) => {
|
|
102
|
-
promises.push(
|
|
103
|
-
promise.then((routeList) => {
|
|
104
|
-
routes = routes.concat(routeList);
|
|
105
|
-
})
|
|
106
|
-
);
|
|
107
|
-
this.pendingPromises.add(promise);
|
|
108
|
-
promise.finally(() => this.pendingPromises.delete(promise));
|
|
138
|
+
const event = new ActivateEvent(this.pendingPromises);
|
|
139
|
+
process.nextTick(() => {
|
|
140
|
+
this.dispatchEvent(event);
|
|
141
|
+
const promises = event.getPromises();
|
|
142
|
+
if (promises.length === 0) {
|
|
143
|
+
this.isActivated = true;
|
|
144
|
+
resolve();
|
|
145
|
+
} else {
|
|
146
|
+
Promise.all(promises).then(() => {
|
|
147
|
+
this.isActivated = true;
|
|
148
|
+
resolve();
|
|
149
|
+
}).catch(reject);
|
|
109
150
|
}
|
|
110
151
|
});
|
|
111
|
-
this.dispatchEvent(event);
|
|
112
|
-
if (promises.length === 0) {
|
|
113
|
-
resolve([]);
|
|
114
|
-
} else {
|
|
115
|
-
Promise.allSettled(promises).then(() => resolve([...new Set(routes)])).catch(reject);
|
|
116
|
-
}
|
|
117
152
|
});
|
|
118
153
|
}
|
|
119
154
|
/**
|
|
@@ -137,32 +172,53 @@ var ServiceWorkerRuntime = class extends EventTarget {
|
|
|
137
172
|
this.isInstalled = false;
|
|
138
173
|
this.isActivated = false;
|
|
139
174
|
this.pendingPromises.clear();
|
|
140
|
-
const listeners
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
delete listeners[type];
|
|
175
|
+
for (const [type, listeners] of this.eventListeners) {
|
|
176
|
+
for (const listener of listeners) {
|
|
177
|
+
super.removeEventListener(type, listener);
|
|
144
178
|
}
|
|
145
179
|
}
|
|
180
|
+
this.eventListeners.clear();
|
|
146
181
|
}
|
|
147
182
|
};
|
|
148
183
|
function createServiceWorkerGlobals(runtime, options = {}) {
|
|
149
184
|
if (options.caches) {
|
|
150
185
|
runtime.caches = options.caches;
|
|
151
186
|
}
|
|
152
|
-
if (options.
|
|
153
|
-
runtime.
|
|
187
|
+
if (options.buckets) {
|
|
188
|
+
runtime.buckets = options.buckets;
|
|
154
189
|
}
|
|
155
|
-
|
|
190
|
+
const skipWaiting = async () => {
|
|
191
|
+
if (options.isDevelopment && options.hotReload) {
|
|
192
|
+
console.info("[ServiceWorker] skipWaiting() - triggering hot reload");
|
|
193
|
+
await options.hotReload();
|
|
194
|
+
} else if (!options.isDevelopment) {
|
|
195
|
+
console.info("[ServiceWorker] skipWaiting() - production graceful restart not implemented");
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const clients = {
|
|
199
|
+
async claim() {
|
|
200
|
+
},
|
|
201
|
+
async get(id) {
|
|
202
|
+
return void 0;
|
|
203
|
+
},
|
|
204
|
+
async matchAll(options2) {
|
|
205
|
+
return [];
|
|
206
|
+
},
|
|
207
|
+
async openWindow(url) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const globals = {
|
|
156
212
|
self: runtime,
|
|
157
213
|
addEventListener: runtime.addEventListener.bind(runtime),
|
|
158
214
|
removeEventListener: runtime.removeEventListener.bind(runtime),
|
|
159
215
|
dispatchEvent: runtime.dispatchEvent.bind(runtime),
|
|
160
|
-
// ServiceWorker-specific globals
|
|
161
|
-
skipWaiting
|
|
162
|
-
clients
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
},
|
|
216
|
+
// ServiceWorker-specific globals with proper implementations
|
|
217
|
+
skipWaiting,
|
|
218
|
+
clients,
|
|
219
|
+
// Platform resources
|
|
220
|
+
...options.buckets && { buckets: options.buckets },
|
|
221
|
+
...options.caches && { caches: options.caches },
|
|
166
222
|
// Standard globals
|
|
167
223
|
console,
|
|
168
224
|
setTimeout,
|
|
@@ -176,8 +232,14 @@ function createServiceWorkerGlobals(runtime, options = {}) {
|
|
|
176
232
|
URL,
|
|
177
233
|
URLSearchParams
|
|
178
234
|
};
|
|
235
|
+
Object.assign(globalThis, globals);
|
|
236
|
+
return globals;
|
|
179
237
|
}
|
|
180
238
|
export {
|
|
239
|
+
ActivateEvent,
|
|
240
|
+
ExtendableEvent,
|
|
241
|
+
FetchEvent,
|
|
242
|
+
InstallEvent,
|
|
181
243
|
ServiceWorkerRuntime,
|
|
182
244
|
createServiceWorkerGlobals
|
|
183
245
|
};
|
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
|
|
171
|
+
listen(): Promise<void>;
|
|
170
172
|
/** Stop the server */
|
|
171
|
-
close(): Promise<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
|
|
174
|
-
/**
|
|
175
|
-
|
|
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
|
|
217
|
+
* SUPPORTING UTILITY - Get filesystem directory handle
|
|
211
218
|
* Maps directly to cloud storage buckets (S3, R2) or local directories
|
|
212
|
-
* @param
|
|
219
|
+
* @param name - Directory name. Use "" for root directory
|
|
213
220
|
*/
|
|
214
|
-
|
|
221
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
215
222
|
}
|
|
216
223
|
/**
|
|
217
224
|
* Platform detection result
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common WorkerPool abstraction based on web standards
|
|
3
|
+
* Provides platform-agnostic worker management for ServiceWorker execution
|
|
4
|
+
*/
|
|
5
|
+
import { CustomCacheStorage } from "@b9g/cache";
|
|
6
|
+
export interface WorkerPoolOptions {
|
|
7
|
+
/** Number of workers in the pool (default: 1) */
|
|
8
|
+
workerCount?: number;
|
|
9
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
10
|
+
requestTimeout?: number;
|
|
11
|
+
/** Enable hot reloading (default: true in development) */
|
|
12
|
+
hotReload?: boolean;
|
|
13
|
+
/** Working directory for file resolution */
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkerMessage {
|
|
17
|
+
type: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
export interface WorkerRequest extends WorkerMessage {
|
|
21
|
+
type: "request";
|
|
22
|
+
request: {
|
|
23
|
+
url: string;
|
|
24
|
+
method: string;
|
|
25
|
+
headers: Record<string, string>;
|
|
26
|
+
body?: any;
|
|
27
|
+
};
|
|
28
|
+
requestId: number;
|
|
29
|
+
}
|
|
30
|
+
export interface WorkerResponse extends WorkerMessage {
|
|
31
|
+
type: "response";
|
|
32
|
+
response: {
|
|
33
|
+
status: number;
|
|
34
|
+
statusText: string;
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
body: string;
|
|
37
|
+
};
|
|
38
|
+
requestId: number;
|
|
39
|
+
}
|
|
40
|
+
export interface WorkerLoadMessage extends WorkerMessage {
|
|
41
|
+
type: "load";
|
|
42
|
+
version: number | string;
|
|
43
|
+
entrypoint?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface WorkerReadyMessage extends WorkerMessage {
|
|
46
|
+
type: "ready" | "worker-ready";
|
|
47
|
+
version?: number | string;
|
|
48
|
+
}
|
|
49
|
+
export interface WorkerErrorMessage extends WorkerMessage {
|
|
50
|
+
type: "error";
|
|
51
|
+
error: string;
|
|
52
|
+
stack?: string;
|
|
53
|
+
requestId?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Common WorkerPool implementation based on web standards
|
|
57
|
+
* Provides round-robin request handling, hot reloading, and cache coordination
|
|
58
|
+
*/
|
|
59
|
+
export declare class WorkerPool {
|
|
60
|
+
private workers;
|
|
61
|
+
private currentWorker;
|
|
62
|
+
private requestId;
|
|
63
|
+
private pendingRequests;
|
|
64
|
+
private options;
|
|
65
|
+
private cacheStorage;
|
|
66
|
+
private appEntrypoint?;
|
|
67
|
+
constructor(cacheStorage: CustomCacheStorage, options?: WorkerPoolOptions, appEntrypoint?: string);
|
|
68
|
+
/**
|
|
69
|
+
* Initialize workers (must be called after construction)
|
|
70
|
+
*/
|
|
71
|
+
init(): Promise<void>;
|
|
72
|
+
private initWorkers;
|
|
73
|
+
private createWorker;
|
|
74
|
+
private handleWorkerMessage;
|
|
75
|
+
private handleResponse;
|
|
76
|
+
private handleError;
|
|
77
|
+
private handleReady;
|
|
78
|
+
/**
|
|
79
|
+
* Platform-specific cache message handling
|
|
80
|
+
* Override this method in platform implementations for custom cache coordination
|
|
81
|
+
*/
|
|
82
|
+
protected handleCacheMessage(message: WorkerMessage): void;
|
|
83
|
+
/**
|
|
84
|
+
* Handle HTTP request using round-robin worker selection
|
|
85
|
+
*/
|
|
86
|
+
handleRequest(request: Request): Promise<Response>;
|
|
87
|
+
/**
|
|
88
|
+
* Reload ServiceWorker with new version (hot reload simulation)
|
|
89
|
+
*/
|
|
90
|
+
reloadWorkers(version?: number | string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Graceful shutdown of all workers
|
|
93
|
+
*/
|
|
94
|
+
terminate(): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Get the number of active workers
|
|
97
|
+
*/
|
|
98
|
+
get workerCount(): number;
|
|
99
|
+
/**
|
|
100
|
+
* Check if the pool is ready to handle requests
|
|
101
|
+
*/
|
|
102
|
+
get ready(): boolean;
|
|
103
|
+
}
|