@statewalker/webrun-http-browser 0.3.3 → 0.3.4
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/LICENSE +1 -1
- package/README.md +321 -51
- package/demo/demo-1.html +109 -194
- package/demo/demo-2.html +118 -299
- package/dist/core/data-calls.d.ts +5 -0
- package/dist/core/data-calls.d.ts.map +1 -0
- package/dist/core/data-channels.d.ts +17 -0
- package/dist/core/data-channels.d.ts.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/message-target.d.ts +16 -0
- package/dist/core/message-target.d.ts.map +1 -0
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/http/http-send-recieve.d.ts +23 -0
- package/dist/http/http-send-recieve.d.ts.map +1 -0
- package/dist/http/index.d.ts +4 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2541 -807
- package/dist/relay/index-sw.d.ts +7 -0
- package/dist/relay/index-sw.d.ts.map +1 -0
- package/dist/relay/index.d.ts +69 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/split-service-url.d.ts +13 -0
- package/dist/relay/split-service-url.d.ts.map +1 -0
- package/dist/relay-sw.d.ts +2 -0
- package/dist/relay-sw.d.ts.map +1 -0
- package/dist/relay-sw.js +878 -0
- package/dist/sw/http-sw-dispatcher.d.ts +28 -0
- package/dist/sw/http-sw-dispatcher.d.ts.map +1 -0
- package/dist/sw/index.d.ts +3 -0
- package/dist/sw/index.d.ts.map +1 -0
- package/dist/sw/sw-dispatcher.d.ts +74 -0
- package/dist/sw/sw-dispatcher.d.ts.map +1 -0
- package/dist/sw-worker.d.ts +2 -0
- package/dist/sw-worker.d.ts.map +1 -0
- package/dist/sw-worker.js +813 -0
- package/dist/sw.d.ts +2 -0
- package/dist/sw.d.ts.map +1 -0
- package/dist/sw.js +1076 -0
- package/package.json +50 -37
- package/public/index.html +61 -97
- package/public/index.js +7 -4
- package/public/sw-worker.js +4 -0
- package/public-relay/heartbeat.js +4 -3
- package/public-relay/relay-sw.js +4 -6
- package/public-relay/relay.html +16 -11
- package/src/core/data-calls.ts +46 -0
- package/src/core/data-channels.ts +224 -0
- package/src/core/index.ts +9 -0
- package/src/core/message-target.ts +18 -0
- package/src/core/registry.ts +54 -0
- package/src/http/http-send-recieve.ts +81 -0
- package/src/http/index.ts +7 -0
- package/src/index.ts +3 -0
- package/src/relay/index-sw.ts +145 -0
- package/src/relay/index.ts +261 -0
- package/src/relay/split-service-url.ts +30 -0
- package/src/relay-sw.ts +7 -0
- package/src/sw/http-sw-dispatcher.ts +122 -0
- package/src/sw/index.ts +2 -0
- package/src/sw/sw-dispatcher.ts +307 -0
- package/src/sw-worker.ts +7 -0
- package/src/sw.ts +1 -0
- package/CHANGELOG.md +0 -1
- package/demo/demo-2 copy.html +0 -598
- package/demo/demo-3.html +0 -167
- package/dist/index-sw-umd.js +0 -798
- package/dist/index-sw-umd.min.js +0 -2
- package/dist/index-sw.js +0 -791
- package/dist/index-sw.min.js +0 -2
- package/dist/index-umd.js +0 -853
- package/dist/index-umd.min.js +0 -2
- package/dist/index.min.js +0 -2
- package/index.js +0 -1
- package/src/core/data-calls.js +0 -36
- package/src/core/data-channels.js +0 -192
- package/src/core/data-send-recieve.js +0 -28
- package/src/core/errors.js +0 -13
- package/src/core/index.js +0 -4
- package/src/http/HttpError.js +0 -59
- package/src/http/http-send-recieve.js +0 -40
- package/src/http/http-stubs.js +0 -135
- package/src/http/index.js +0 -3
- package/src/http/readable-streams.js +0 -33
- package/src/index.js +0 -7
- package/src/relay/index-sw.js +0 -216
- package/src/relay/index.js +0 -263
- package/src/relay/splitServiceUrl.js +0 -24
- package/src/sw/http-sw-dispatcher.js +0 -128
- package/src/sw/sw-dispatcher.js +0 -264
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { HttpHandler } from "@statewalker/webrun-http-streams";
|
|
2
|
+
import { handleHttpRequests, sendHttpRequest } from "../http/http-send-recieve.js";
|
|
3
|
+
import {
|
|
4
|
+
SwPortDispatcher,
|
|
5
|
+
type SwPortDispatcherOptions,
|
|
6
|
+
SwPortHandler,
|
|
7
|
+
type SwPortHandlerOptions,
|
|
8
|
+
} from "./sw-dispatcher.js";
|
|
9
|
+
|
|
10
|
+
export interface SwHttpAdapterOptions extends Omit<SwPortHandlerOptions, "bindPort"> {
|
|
11
|
+
bindPort?: SwPortHandlerOptions["bindPort"];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SwHttpRegistration {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
prefix: string;
|
|
17
|
+
remove(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class SwHttpAdapter extends SwPortHandler {
|
|
21
|
+
private readonly _handlers = new Map<string, HttpHandler>();
|
|
22
|
+
private _cleanupRequestChannel?: () => void;
|
|
23
|
+
|
|
24
|
+
constructor(options: SwHttpAdapterOptions) {
|
|
25
|
+
super({
|
|
26
|
+
bindPort: () => {},
|
|
27
|
+
...options,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected override async _setCommunicationPort(port: MessagePort): Promise<void> {
|
|
32
|
+
this._cleanupRequestChannel?.();
|
|
33
|
+
this._cleanupRequestChannel = handleHttpRequests(port, this._handleHttpRequest.bind(this));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async _handleHttpRequest(request: Request): Promise<Response> {
|
|
37
|
+
const requestUrl = request.url;
|
|
38
|
+
for (const [urlPrefix, handler] of this._handlers) {
|
|
39
|
+
if (requestUrl.indexOf(urlPrefix) === 0) {
|
|
40
|
+
return handler(request);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return new Response(null, { status: 404, statusText: "Error 404: Not found" });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Registers an HTTP handler on a prefix. Returns the resulting base URL and a
|
|
48
|
+
* disposer.
|
|
49
|
+
*/
|
|
50
|
+
async register(prefix: string, handler: HttpHandler): Promise<SwHttpRegistration> {
|
|
51
|
+
const cleanPrefix = `./${(prefix ?? "").replace(/^[./]+/, "")}`;
|
|
52
|
+
const baseUrl = `${new URL(cleanPrefix, this.rootUrl)}`;
|
|
53
|
+
this._handlers.set(baseUrl, handler);
|
|
54
|
+
const handlers = this._handlers;
|
|
55
|
+
return {
|
|
56
|
+
baseUrl,
|
|
57
|
+
prefix: cleanPrefix,
|
|
58
|
+
async remove() {
|
|
59
|
+
handlers.delete(baseUrl);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class SwHttpDispatcher extends SwPortDispatcher {
|
|
66
|
+
start(): void {
|
|
67
|
+
super.start();
|
|
68
|
+
this.self.addEventListener("fetch", this._handleFetchEvent.bind(this));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private _handleFetchEvent(event: FetchEvent): void {
|
|
72
|
+
event.respondWith(
|
|
73
|
+
(async (): Promise<Response> => {
|
|
74
|
+
const request = event.request;
|
|
75
|
+
try {
|
|
76
|
+
const requestUrl = request.url;
|
|
77
|
+
const rootUrl = this.scope;
|
|
78
|
+
if (requestUrl.indexOf(rootUrl) === 0) {
|
|
79
|
+
const key = requestUrl.substring(rootUrl.length).replace(/^\/?([^/]+).*$/, "$1");
|
|
80
|
+
const channelInfo = await this.loadChannelInfo(key);
|
|
81
|
+
if (channelInfo?.port) {
|
|
82
|
+
return await sendHttpRequest(channelInfo.port, request);
|
|
83
|
+
}
|
|
84
|
+
// Key was registered at some point on this SW but has no live
|
|
85
|
+
// handler right now (owner tab closed). Return 404 instead of
|
|
86
|
+
// falling through to the network — the alternative (a dev
|
|
87
|
+
// server's SPA fallback) serves the app shell for every site
|
|
88
|
+
// URL and creates a confusing refresh-loop where stale tabs
|
|
89
|
+
// keep resurrecting registrations.
|
|
90
|
+
if (this.isClaimedKey(key)) {
|
|
91
|
+
return new Response(null, {
|
|
92
|
+
status: 404,
|
|
93
|
+
statusText: "Not Found (no active handler)",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// URL is outside scope, or the first segment is unrelated to any
|
|
98
|
+
// registered site (Vite's /assets/*, /favicon.ico, etc.) — pass
|
|
99
|
+
// the event's Request straight through. Reconstructing it drops
|
|
100
|
+
// `mode`, which makes the constructor throw
|
|
101
|
+
// `'only-if-cached' can be set only with 'same-origin' mode` for
|
|
102
|
+
// navigation-style requests Chrome issues with that cache mode.
|
|
103
|
+
return await fetch(request);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(error);
|
|
106
|
+
return new Response(null, {
|
|
107
|
+
status: 500,
|
|
108
|
+
statusText: "Error 500: Internal error",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
})(),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function startHttpDispatcher(options: SwPortDispatcherOptions): () => void {
|
|
117
|
+
const dispatcher = new SwHttpDispatcher(options);
|
|
118
|
+
dispatcher.start();
|
|
119
|
+
return () => {
|
|
120
|
+
void dispatcher.stop();
|
|
121
|
+
};
|
|
122
|
+
}
|
package/src/sw/index.ts
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { get, set } from "idb-keyval";
|
|
2
|
+
import { callChannel, handleChannelCalls } from "../core/data-calls.js";
|
|
3
|
+
import { newRegistry } from "../core/registry.js";
|
|
4
|
+
|
|
5
|
+
export interface SwPortHandlerOptions {
|
|
6
|
+
key: string;
|
|
7
|
+
scope?: string;
|
|
8
|
+
serviceWorkerUrl?: string;
|
|
9
|
+
bindPort: (port: MessagePort) => void | Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ChannelInfo {
|
|
13
|
+
key: string;
|
|
14
|
+
clientId?: string;
|
|
15
|
+
port?: MessagePort;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Page-side counterpart: registers a ServiceWorker and sets up a
|
|
20
|
+
* MessageChannel with it, re-establishing the connection after SW wake-ups.
|
|
21
|
+
*/
|
|
22
|
+
export class SwPortHandler {
|
|
23
|
+
readonly options: SwPortHandlerOptions;
|
|
24
|
+
private _serviceWorkerUrl?: string;
|
|
25
|
+
private _serviceWorker?: ServiceWorker;
|
|
26
|
+
private _registrationPromise?: Promise<void>;
|
|
27
|
+
private _cleanupRegistrations?: () => void;
|
|
28
|
+
|
|
29
|
+
constructor(options: SwPortHandlerOptions) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
if (!this.key) throw new Error("Key is not defined.");
|
|
32
|
+
if (!this.scope) throw new Error("Scope is not defined");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get key(): string {
|
|
36
|
+
return this.options.key;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get scope(): string {
|
|
40
|
+
return this.options.scope ?? new URL("./", this.serviceWorkerUrl).pathname;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get rootUrl(): URL {
|
|
44
|
+
return new URL(this.scope, import.meta.url);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get serviceWorkerUrl(): string {
|
|
48
|
+
if (!this._serviceWorkerUrl) {
|
|
49
|
+
const url = this.options.serviceWorkerUrl
|
|
50
|
+
? new URL(this.options.serviceWorkerUrl)
|
|
51
|
+
: new URL("./index-sw.js", this.rootUrl);
|
|
52
|
+
this._serviceWorkerUrl = `${url}`;
|
|
53
|
+
}
|
|
54
|
+
return this._serviceWorkerUrl;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected async _newCommunicationChannel(): Promise<[MessagePort, MessagePort]> {
|
|
58
|
+
const messageChannel = new MessageChannel();
|
|
59
|
+
return [messageChannel.port1, messageChannel.port2];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected async _setCommunicationPort(port: MessagePort): Promise<void> {
|
|
63
|
+
await this.options.bindPort(port);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected async _updateCommunicationChannel(): Promise<void> {
|
|
67
|
+
if (!this._serviceWorker) return;
|
|
68
|
+
const [port1, port2] = await this._newCommunicationChannel();
|
|
69
|
+
await this._setCommunicationPort(port1);
|
|
70
|
+
await callChannel(
|
|
71
|
+
this._serviceWorker,
|
|
72
|
+
"UPDATE_COMMUNICATION_PORT",
|
|
73
|
+
this._getRegistrationInfo(),
|
|
74
|
+
port2,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected _getRegistrationInfo(): ChannelInfo {
|
|
79
|
+
return { key: this.key };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async start(): Promise<void> {
|
|
83
|
+
if (!this._registrationPromise) {
|
|
84
|
+
this._registrationPromise = (async () => {
|
|
85
|
+
const [register, cleanup] = newRegistry();
|
|
86
|
+
this._cleanupRegistrations = cleanup;
|
|
87
|
+
register(() => {
|
|
88
|
+
this._cleanupRegistrations = undefined;
|
|
89
|
+
});
|
|
90
|
+
const registration = await navigator.serviceWorker.register(this.serviceWorkerUrl, {
|
|
91
|
+
scope: this.scope,
|
|
92
|
+
});
|
|
93
|
+
register(() => registration.unregister());
|
|
94
|
+
|
|
95
|
+
register(
|
|
96
|
+
handleChannelCalls(
|
|
97
|
+
navigator.serviceWorker,
|
|
98
|
+
"UPDATE_COMMUNICATION_PORT",
|
|
99
|
+
async (_event, _params, port: MessagePort) => {
|
|
100
|
+
await this._setCommunicationPort(port);
|
|
101
|
+
return this._getRegistrationInfo();
|
|
102
|
+
},
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
this._serviceWorker = await getServiceWorkerController();
|
|
107
|
+
await awaitServiceWorkerActivation(this._serviceWorker);
|
|
108
|
+
await this._updateCommunicationChannel();
|
|
109
|
+
})();
|
|
110
|
+
}
|
|
111
|
+
return this._registrationPromise;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async stop(): Promise<void> {
|
|
115
|
+
this._cleanupRegistrations?.();
|
|
116
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
117
|
+
for (const registration of registrations) {
|
|
118
|
+
try {
|
|
119
|
+
await registration.unregister();
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.log("Service Worker registration failed: ", error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getServiceWorkerController(): Promise<ServiceWorker> {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const container = navigator.serviceWorker;
|
|
130
|
+
if (container.controller) {
|
|
131
|
+
resolve(container.controller);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const onChange = () => {
|
|
135
|
+
if (!container.controller) return;
|
|
136
|
+
resolve(container.controller);
|
|
137
|
+
container.removeEventListener("controllerchange", onChange);
|
|
138
|
+
};
|
|
139
|
+
container.addEventListener("controllerchange", onChange);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function awaitServiceWorkerActivation(worker: ServiceWorker): Promise<void> {
|
|
144
|
+
return new Promise((resolve) => {
|
|
145
|
+
if (worker.state === "activated") {
|
|
146
|
+
resolve();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const onStateChange = () => {
|
|
150
|
+
if (worker.state !== "activated") return;
|
|
151
|
+
worker.removeEventListener("statechange", onStateChange);
|
|
152
|
+
resolve();
|
|
153
|
+
};
|
|
154
|
+
worker.addEventListener("statechange", onStateChange);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface SwPortDispatcherOptions {
|
|
159
|
+
self: ServiceWorkerGlobalScope;
|
|
160
|
+
log?: (...args: unknown[]) => void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* ServiceWorker-side counterpart: maintains an index of connected clients keyed by
|
|
165
|
+
* clientId, persisted in IndexedDB so it can reclaim ports across SW restarts.
|
|
166
|
+
*/
|
|
167
|
+
export class SwPortDispatcher {
|
|
168
|
+
readonly self: ServiceWorkerGlobalScope;
|
|
169
|
+
readonly log: (...args: unknown[]) => void;
|
|
170
|
+
readonly handlersIndex = new Map<string, ChannelInfo>();
|
|
171
|
+
/**
|
|
172
|
+
* Set of keys that were registered at some point on this SW (sticky —
|
|
173
|
+
* survives client closures + SW wake-ups via IndexedDB). Lets the fetch
|
|
174
|
+
* handler distinguish "site URL whose owner is gone → 404" from
|
|
175
|
+
* "unknown URL under scope → pass through to the network".
|
|
176
|
+
*/
|
|
177
|
+
readonly claimedKeys = new Set<string>();
|
|
178
|
+
private _cleanup?: () => void;
|
|
179
|
+
private _activationPromise?: Promise<void>;
|
|
180
|
+
|
|
181
|
+
constructor({ self, log = () => {} }: SwPortDispatcherOptions) {
|
|
182
|
+
this.self = self;
|
|
183
|
+
this.log = log;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
get scope(): string {
|
|
187
|
+
return `${new URL("./", this.self.location.href)}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Look up the active channel for a key. Returns `undefined` when there
|
|
192
|
+
* is no registration (or the registering client has gone away — stale
|
|
193
|
+
* entries are pruned in place).
|
|
194
|
+
*/
|
|
195
|
+
async loadChannelInfo(key: string): Promise<ChannelInfo | undefined> {
|
|
196
|
+
await this._checkActivation();
|
|
197
|
+
for (const channelInfo of this.handlersIndex.values()) {
|
|
198
|
+
if (channelInfo.key !== key) continue;
|
|
199
|
+
const clientId = channelInfo.clientId;
|
|
200
|
+
if (!clientId) continue;
|
|
201
|
+
const client = await this.self.clients.get(clientId);
|
|
202
|
+
if (!client) {
|
|
203
|
+
// Owner tab closed: drop the stale entry and keep scanning for
|
|
204
|
+
// another client that still claims this key.
|
|
205
|
+
this.handlersIndex.delete(clientId);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
return channelInfo;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Whether `key` has ever been registered on this SW. */
|
|
214
|
+
isClaimedKey(key: string): boolean {
|
|
215
|
+
return this.claimedKeys.has(key);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
start(): void {
|
|
219
|
+
this._cleanup = handleChannelCalls(
|
|
220
|
+
this.self,
|
|
221
|
+
"UPDATE_COMMUNICATION_PORT",
|
|
222
|
+
async (event, channelInfo, port: MessagePort) => {
|
|
223
|
+
const client = event.source as Client | null;
|
|
224
|
+
const clientId = client?.id;
|
|
225
|
+
this.log("[UPDATE_COMMUNICATION_PORT]", clientId, channelInfo);
|
|
226
|
+
await this._updateChannelInfo({ ...(channelInfo as ChannelInfo), port, clientId });
|
|
227
|
+
return { ...(channelInfo as ChannelInfo) };
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
this.self.addEventListener("install", (event) => {
|
|
232
|
+
this.log("Skip waiting on install.", event);
|
|
233
|
+
this.self.skipWaiting();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
this.self.addEventListener("activate", (event) => {
|
|
237
|
+
this.log("Claim control over all clients.", event);
|
|
238
|
+
event.waitUntil(
|
|
239
|
+
(async () => {
|
|
240
|
+
await this.self.clients.claim();
|
|
241
|
+
await this._checkActivation();
|
|
242
|
+
})(),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async stop(): Promise<void> {
|
|
248
|
+
if (this._cleanup) {
|
|
249
|
+
this._cleanup();
|
|
250
|
+
this._cleanup = undefined;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private _checkActivation(): Promise<void> {
|
|
255
|
+
if (!this._activationPromise) {
|
|
256
|
+
this._activationPromise = this._activate();
|
|
257
|
+
}
|
|
258
|
+
return this._activationPromise;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private async _activate(): Promise<void> {
|
|
262
|
+
this.log("[checkActivation]");
|
|
263
|
+
const clientsIndex = await this._loadClientIds();
|
|
264
|
+
for (const [clientId, client] of clientsIndex.entries()) {
|
|
265
|
+
const messageChannel = new MessageChannel();
|
|
266
|
+
const port = messageChannel.port1;
|
|
267
|
+
const channelInfo = await callChannel<ChannelInfo>(
|
|
268
|
+
client,
|
|
269
|
+
"UPDATE_COMMUNICATION_PORT",
|
|
270
|
+
{},
|
|
271
|
+
messageChannel.port2,
|
|
272
|
+
);
|
|
273
|
+
await this._updateChannelInfo({ ...channelInfo, port, clientId });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private async _updateChannelInfo(channelInfo: ChannelInfo): Promise<void> {
|
|
278
|
+
this.log("[updateClientInfo]", channelInfo);
|
|
279
|
+
if (!channelInfo.clientId) return;
|
|
280
|
+
this.handlersIndex.set(channelInfo.clientId, channelInfo);
|
|
281
|
+
if (channelInfo.key && !this.claimedKeys.has(channelInfo.key)) {
|
|
282
|
+
this.claimedKeys.add(channelInfo.key);
|
|
283
|
+
await set("claimedKeys", [...this.claimedKeys].sort());
|
|
284
|
+
}
|
|
285
|
+
await this._updateClientIds([]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private async _loadClientIds(): Promise<Map<string, Client>> {
|
|
289
|
+
const storedKeys = ((await get<string[]>("claimedKeys")) ?? []) as string[];
|
|
290
|
+
for (const key of storedKeys) this.claimedKeys.add(key);
|
|
291
|
+
const stored = ((await get<string[]>("clientIds")) ?? []) as string[];
|
|
292
|
+
return await this._updateClientIds(new Set(stored));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private async _updateClientIds(clientIds: Iterable<string>): Promise<Map<string, Client>> {
|
|
296
|
+
const ids = new Set(clientIds);
|
|
297
|
+
for (const clientId of this.handlersIndex.keys()) ids.add(clientId);
|
|
298
|
+
const index = new Map<string, Client>();
|
|
299
|
+
for (const clientId of ids) {
|
|
300
|
+
const client = await this.self.clients.get(clientId);
|
|
301
|
+
if (client) index.set(clientId, client);
|
|
302
|
+
else this.handlersIndex.delete(clientId);
|
|
303
|
+
}
|
|
304
|
+
await set("clientIds", [...index.keys()].sort());
|
|
305
|
+
return index;
|
|
306
|
+
}
|
|
307
|
+
}
|
package/src/sw-worker.ts
ADDED
package/src/sw.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./sw/index.js";
|
package/CHANGELOG.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# @statewalker/webrun-http-browser
|