@statewalker/webrun-http-browser 0.1.0 → 0.2.0

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.
@@ -1,23 +1,8 @@
1
- // @statewalker/webrun-http-browser v0.1.0 https://github.com/statewalker/statewalker-webrun-http-browser Copyright 2023 Mikhail Kotelnikov
2
- (function (global, factory) {
3
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.statewalker = global.statewalker || {}, global.statewalker["webrun-http-browser"] = global.statewalker["webrun-http-browser"] || {})));
6
- })(this, (function (exports) { 'use strict';
7
-
8
- function serializeError(error) {
9
- return Object.assign({
10
- message: error,
11
- stack: error.stack,
12
- }, error);
13
- }
14
-
15
- function deserializeError(error) {
16
- if (typeof error === "string") {
17
- error = { message: error };
18
- }
19
- return Object.assign(new Error(error.message), error);
20
- }
1
+ // @statewalker/webrun-http-browser v0.2.0 https://github.com/statewalker/statewalker-webrun-http-browser Copyright 2023 Mikhail Kotelnikov
2
+ (function (factory) {
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ factory();
5
+ })((function () { 'use strict';
21
6
 
22
7
  // See https://github.com/agenjs/agen-utils/blob/main/src/iterator.js
23
8
  async function* iterate(init, queue = []) {
@@ -57,6 +42,126 @@ async function* iterate(init, queue = []) {
57
42
  }
58
43
  }
59
44
 
45
+ function newRegistry(onError = console.error) {
46
+ let counter = 0;
47
+ const registrations = {};
48
+ const register = (action) => {
49
+ const id = counter++;
50
+ return (registrations[id] = Object.assign(
51
+ function (skip) {
52
+ try {
53
+ delete registrations[id];
54
+ return !skip && action && action();
55
+ } catch (error) {
56
+ onError(error);
57
+ }
58
+ },
59
+ { action }
60
+ ));
61
+ };
62
+ const unregister = (action) =>
63
+ Object.values(registrations).forEach((r) => r.action === action && r(true));
64
+ const clear = () => Object.values(registrations).forEach((r) => r());
65
+ return Object.assign([register, clear, unregister], {
66
+ register,
67
+ clear,
68
+ unregister
69
+ });
70
+ }
71
+
72
+ function promisifyRequest(request) {
73
+ return new Promise((resolve, reject) => {
74
+ // @ts-ignore - file size hacks
75
+ request.oncomplete = request.onsuccess = () => resolve(request.result);
76
+ // @ts-ignore - file size hacks
77
+ request.onabort = request.onerror = () => reject(request.error);
78
+ });
79
+ }
80
+ function createStore(dbName, storeName) {
81
+ const request = indexedDB.open(dbName);
82
+ request.onupgradeneeded = () => request.result.createObjectStore(storeName);
83
+ const dbp = promisifyRequest(request);
84
+ return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
85
+ }
86
+ let defaultGetStoreFunc;
87
+ function defaultGetStore() {
88
+ if (!defaultGetStoreFunc) {
89
+ defaultGetStoreFunc = createStore('keyval-store', 'keyval');
90
+ }
91
+ return defaultGetStoreFunc;
92
+ }
93
+ /**
94
+ * Get a value by its key.
95
+ *
96
+ * @param key
97
+ * @param customStore Method to get a custom store. Use with caution (see the docs).
98
+ */
99
+ function get(key, customStore = defaultGetStore()) {
100
+ return customStore('readonly', (store) => promisifyRequest(store.get(key)));
101
+ }
102
+ /**
103
+ * Set a value with a key.
104
+ *
105
+ * @param key
106
+ * @param value
107
+ * @param customStore Method to get a custom store. Use with caution (see the docs).
108
+ */
109
+ function set(key, value, customStore = defaultGetStore()) {
110
+ return customStore('readwrite', (store) => {
111
+ store.put(value, key);
112
+ return promisifyRequest(store.transaction);
113
+ });
114
+ }
115
+
116
+ function serializeError(error) {
117
+ return Object.assign({
118
+ message: error,
119
+ stack: error.stack,
120
+ }, error);
121
+ }
122
+
123
+ function deserializeError(error) {
124
+ if (typeof error === "string") {
125
+ error = { message: error };
126
+ }
127
+ return Object.assign(new Error(error.message), error);
128
+ }
129
+
130
+ async function callChannel(mainChannel, callType, params, ...transfers) {
131
+ return new Promise((resolve, reject) => {
132
+ const channel = new MessageChannel();
133
+ channel.port1.onmessage = (ev) => {
134
+ const { result, error } = ev.data;
135
+ if (error) reject(deserializeError(error));
136
+ else resolve(result);
137
+ };
138
+ mainChannel.postMessage({ type: callType, params }, [
139
+ channel.port2,
140
+ ...transfers,
141
+ ]);
142
+ });
143
+ }
144
+
145
+ function handleChannelCalls(mainChannel, callType, handler) {
146
+ const messageListener = async (event) => {
147
+ if (!event.data || event.data.type !== callType) return;
148
+ const [port, ...transfers] = event.ports;
149
+ let response = {};
150
+ try {
151
+ const params = event.data.params;
152
+ response.result = await handler(event, params, ...transfers);
153
+ } catch (error) {
154
+ response.error = serializeError(error);
155
+ }
156
+ port.postMessage(response);
157
+ };
158
+ mainChannel.addEventListener("message", messageListener);
159
+ try {
160
+ mainChannel.start && mainChannel.start();
161
+ } catch (e) {}
162
+ return () => mainChannel.removeEventListener("message", messageListener);
163
+ }
164
+
60
165
  async function* recieveData(onMessage) {
61
166
  yield* iterate((iterator) => {
62
167
  return onMessage(async ({ done = true, value, error } = {}) => {
@@ -121,7 +226,10 @@ function newChannel(port) {
121
226
  await listener(data);
122
227
  }
123
228
  };
124
- const channel = newInvokationChannel(port, notifyAll);
229
+ const channel = newInvokationChannel({
230
+ port,
231
+ handler: notifyAll,
232
+ });
125
233
 
126
234
  const start = () => channel.start();
127
235
 
@@ -159,15 +267,20 @@ function newChannel(port) {
159
267
  };
160
268
  }
161
269
 
162
- function newInvokationChannel(port, handler = () => {
163
- throw new Error("Handler not implemented");
270
+ function newInvokationChannel({
271
+ port,
272
+ handler = () => {
273
+ throw new Error("Handler not implemented");
274
+ },
275
+ onError = console.error,
276
+ newCallId = () =>
277
+ newInvokationChannel.__counter = (newInvokationChannel.__counter || 0) + 1,
164
278
  }) {
165
279
  const MESSAGE_TYPE_REQUEST = "REQUEST";
166
280
  const MESSAGE_TYPE_RESPONSE = "RESPONSE";
167
281
  const EVENT_MESSAGE = "message";
168
282
 
169
283
  const requests = {};
170
- let requestCounter = 0;
171
284
  const listener = async (event) => {
172
285
  const data = event.data || {};
173
286
  const { type, callId } = data;
@@ -204,17 +317,25 @@ function newInvokationChannel(port, handler = () => {
204
317
  };
205
318
 
206
319
  const start = async () => {
207
- port.addEventListener(EVENT_MESSAGE, listener);
208
- await port.start();
320
+ try {
321
+ port.addEventListener(EVENT_MESSAGE, listener);
322
+ port.start && await port.start();
323
+ } catch (e) {
324
+ onError(e);
325
+ }
209
326
  };
210
327
 
211
328
  const close = async () => {
212
- port.removeEventListener(EVENT_MESSAGE, listener);
213
- await port.close();
329
+ try {
330
+ port.removeEventListener(EVENT_MESSAGE, listener);
331
+ port.close && await port.close();
332
+ } catch (e) {
333
+ onError(e);
334
+ }
214
335
  };
215
336
 
216
337
  const invoke = async function (request = {}, ...transfers) {
217
- const callId = ++requestCounter;
338
+ const callId = newCallId();
218
339
  return new Promise((resolve, reject) => {
219
340
  try {
220
341
  requests[callId] = { resolve, reject };
@@ -372,280 +493,302 @@ async function sendHttpRequest(communicationPort, request) {
372
493
  return await clientStub(request);
373
494
  }
374
495
 
375
- async function callChannel(mainChannel, callType, params, ...transfers) {
376
- return new Promise((resolve, reject) => {
377
- const channel = new MessageChannel();
378
- channel.port1.onmessage = (ev) => {
379
- const { result, error } = ev.data;
380
- if (error) reject(deserializeError(error));
381
- else resolve(result);
382
- };
383
- mainChannel.postMessage({ type: callType, params }, [
384
- channel.port2,
385
- ...transfers,
386
- ]);
387
- });
388
- }
389
-
390
- function handleChannelCalls(mainChannel, callType, handler) {
391
- const messageListener = async (event) => {
392
- if (!event.data || event.data.type !== callType) return;
393
- const [port, ...transfers] = event.ports;
394
- let response = {};
395
- try {
396
- const params = event.data.params;
397
- response.result = await handler(event, params, ...transfers);
398
- } catch (error) {
399
- response.error = serializeError(error);
400
- }
401
- port.postMessage(response);
496
+ function splitServiceUrl(url, separator = "~") {
497
+ let key = "";
498
+ let path = "";
499
+ let str = url + "";
500
+ const idx = str.indexOf(separator);
501
+ let baseUrl = "";
502
+ if (idx >= 0) {
503
+ baseUrl = str.substring(0, idx + separator.length);
504
+ str.substring(idx + separator.length)
505
+ .replace(/^([^\/]+)/, (match, $1) => {
506
+ baseUrl += match;
507
+ if (baseUrl.length < str.length) baseUrl += "/";
508
+ key = $1;
509
+ path = str.substring(baseUrl.length);
510
+ return "";
511
+ });
512
+ }
513
+ return {
514
+ url,
515
+ key,
516
+ baseUrl,
517
+ path,
402
518
  };
403
- mainChannel.addEventListener("message", messageListener);
404
- try {
405
- mainChannel.start && mainChannel.start();
406
- } catch (e) {}
407
- return () => mainChannel.removeEventListener("message", messageListener);
408
- }
409
-
410
- function promisifyRequest(request) {
411
- return new Promise((resolve, reject) => {
412
- // @ts-ignore - file size hacks
413
- request.oncomplete = request.onsuccess = () => resolve(request.result);
414
- // @ts-ignore - file size hacks
415
- request.onabort = request.onerror = () => reject(request.error);
416
- });
417
- }
418
- function createStore(dbName, storeName) {
419
- const request = indexedDB.open(dbName);
420
- request.onupgradeneeded = () => request.result.createObjectStore(storeName);
421
- const dbp = promisifyRequest(request);
422
- return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
423
- }
424
- let defaultGetStoreFunc;
425
- function defaultGetStore() {
426
- if (!defaultGetStoreFunc) {
427
- defaultGetStoreFunc = createStore('keyval-store', 'keyval');
428
- }
429
- return defaultGetStoreFunc;
430
- }
431
- /**
432
- * Get a value by its key.
433
- *
434
- * @param key
435
- * @param customStore Method to get a custom store. Use with caution (see the docs).
436
- */
437
- function get(key, customStore = defaultGetStore()) {
438
- return customStore('readonly', (store) => promisifyRequest(store.get(key)));
439
- }
440
- /**
441
- * Set a value with a key.
442
- *
443
- * @param key
444
- * @param value
445
- * @param customStore Method to get a custom store. Use with caution (see the docs).
446
- */
447
- function set(key, value, customStore = defaultGetStore()) {
448
- return customStore('readwrite', (store) => {
449
- store.put(value, key);
450
- return promisifyRequest(store.transaction);
451
- });
452
519
  }
453
520
 
454
- class SwPortDispatcher {
455
- constructor({ self, log }) {
456
- this.self = self;
457
- this.log = log;
458
- // Contains mapping between path prefixes and clientIds
459
- this.handlersIndex = new Map();
521
+ class HttpError extends Error {
522
+ constructor(options) {
523
+ super(options.message);
524
+ this.status = options.status;
525
+ this.statusText = options.statusText;
460
526
  }
461
-
462
- get scope() {
463
- return new URL("./", (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (document.currentScript && document.currentScript.src || new URL('index-sw-umd.js', document.baseURI).href))) + "";
527
+ getResponseOptions(options = {}) {
528
+ return Object.assign(this.toJson(), options);
529
+ }
530
+ toJson() {
531
+ return {
532
+ status: this.status,
533
+ statusText: this.statusText,
534
+ message: this.message,
535
+ };
464
536
  }
465
537
 
466
- async loadChannelInfo(key) {
467
- await this._checkActivation();
468
- let result;
469
- for (let channelInfo of this.handlersIndex.values()) {
470
- if (channelInfo.key !== key) continue;
471
- const clientId = channelInfo.clientId;
472
- const client = await self.clients.get(clientId);
473
- if (!client) {
474
- // Client gone. Remove the corresponding handlers from the index.
475
- this.handlersIndex.delete(clientId);
476
- throw Object.assign(new Error("Error 410: Resource Gone"), {
477
- status: 410,
478
- });
479
- }
480
- result = channelInfo;
481
- break;
538
+ static fromError(error) {
539
+ if (!(error instanceof HttpError)) {
540
+ error = new HttpError(Object.assign({
541
+ status: 500,
542
+ statusText: "Bad Request",
543
+ reason: error.message,
544
+ }));
482
545
  }
483
- return result;
546
+ return error;
484
547
  }
485
548
 
486
- start() {
487
- this._cleanup = handleChannelCalls(
488
- this.self,
489
- "UPDATE_COMMUNICATION_PORT",
490
- async (event, channelInfo, port) => {
491
- const clientId = event.source.id;
492
- this.log("[UPDATE_COMMUNICATION_PORT]", clientId, channelInfo);
493
- await this._updateChannelInfo({ ...channelInfo, port, clientId });
494
- return { ...channelInfo };
495
- },
496
- );
497
- this.self.onmessage = () => {};
498
-
499
- this.self.addEventListener("install", (event) => {
500
- this.log("Skip waiting on install.", event);
501
- this.self.skipWaiting();
549
+ static errorResourceNotFound(options = {}) {
550
+ return new HttpError({
551
+ status: 404,
552
+ statusText: "Error 404: Resource not found",
553
+ ...options,
502
554
  });
503
-
504
- this.self.addEventListener("activate", async (event) => {
505
- this.log(
506
- "Claim control over all clients.",
507
- event,
508
- clients,
509
- this.self.clients,
510
- );
511
- event.waitUntil((async () => {
512
- await this.self.clients.claim();
513
- await this._checkActivation();
514
- })());
555
+ }
556
+ static errorForbidden(options = {}) {
557
+ return new HttpError({
558
+ status: 403,
559
+ statusText: "Error 403: Forbidden",
560
+ ...options,
515
561
  });
516
562
  }
517
563
 
518
- async stop() {
519
- if (this._cleanup) {
520
- await this._cleanup();
521
- this._cleanup = undefined;
522
- }
564
+ static errorResourceGone(options = {}) {
565
+ return new HttpError({
566
+ status: 410,
567
+ statusText: "Error 410: Resource Gone",
568
+ ...options,
569
+ });
523
570
  }
524
571
 
525
- async _checkActivation() {
526
- return this._activationPromise = this._activationPromise ||
527
- this._activate();
572
+ static errorInternalError(options) {
573
+ return new HttpError({
574
+ ...options,
575
+ status: 500,
576
+ statusText: "Error 500: Internal error",
577
+ });
528
578
  }
579
+ }
529
580
 
530
- async _activate() {
531
- this.log("[checkActivation]");
532
- let clientsIndex = await this._loadClientIds();
533
- for (let [clientId, client] of clientsIndex.entries()) {
534
- const messageChannel = new MessageChannel();
535
- const port = messageChannel.port1;
536
- const channelInfo = await callChannel(
537
- client,
538
- "UPDATE_COMMUNICATION_PORT",
539
- {},
540
- messageChannel.port2,
541
- );
542
- await this._updateChannelInfo({ ...channelInfo, port, clientId });
543
- }
544
- }
581
+ function startRelayServiceWorker(self) {
582
+ const [register, clear] = newRegistry();
545
583
 
546
- async _updateChannelInfo(channelInfo) {
547
- this.log("[updateClientInfo]", channelInfo);
548
- this.handlersIndex.set(channelInfo.clientId, channelInfo);
549
- await this._updateClientIds([]);
584
+ if (typeof self.skipWaiting === "function") {
585
+ console.log("self.skipWaiting() is supported.");
586
+ self.addEventListener("install", (e) => {
587
+ // See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-global-scope-skipwaiting
588
+ e.waitUntil(self.skipWaiting());
589
+ });
590
+ } else {
591
+ console.log("self.skipWaiting() is not supported.");
550
592
  }
551
593
 
552
- async _loadClientIds() {
553
- const clientIds = new Set((await get("clientIds")) || []);
554
- return await this._updateClientIds(clientIds);
594
+ if (self.clients && (typeof self.clients.claim === "function")) {
595
+ console.log("self.clients.claim() is supported.");
596
+ self.addEventListener("activate", (e) => {
597
+ // See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#clients-claim-method
598
+ e.waitUntil(self.clients.claim());
599
+ });
600
+ } else {
601
+ console.log("self.clients.claim() is not supported.");
555
602
  }
556
603
 
557
- async _updateClientIds(clientIds) {
558
- clientIds = new Set(clientIds);
559
- for (let clientId of this.handlersIndex.keys()) {
560
- clientIds.add(clientId);
604
+ const clientsRegistry = newClientsRegistry({
605
+ self,
606
+ get,
607
+ set,
608
+ });
609
+
610
+ // This listener handles the following message types:
611
+ // * registration: registers new clients as service providers
612
+ // * handles service calls rounting:
613
+ // - it gets the clientKey defined in the call
614
+ // - it uses the serviceKey to get the service-specifc channel
615
+ // - it creates a new message channel and sent its ports to peers
616
+
617
+ register(handleChannelCalls(self, "REGISTER", async (event, data) => {
618
+ return await clientsRegistry.addClient(data.key, event.source);
619
+ }));
620
+ register(handleChannelCalls(self, "UNREGISTER", async (event, data) => {
621
+ return await clientsRegistry.removeClient(data.key);
622
+ }));
623
+
624
+ // Transfers all parameters (and ports) to the target client.
625
+ register(handleChannelCalls(self, "CONNECT", async (event, data, port) => {
626
+ const { key } = data;
627
+ const client = await clientsRegistry.getClient(key);
628
+ if (!client) {
629
+ throw new Error(`Target client was not found. Target key: "${key}".`);
630
+ }
631
+ return await callChannel(client, "CONNECT", data, port);
632
+ }));
633
+
634
+ const fetchListener = (event) => {
635
+ const request = event.request;
636
+ const params = splitServiceUrl(request.url);
637
+ const { key } = params;
638
+ if (key) {
639
+ const handler = async () => {
640
+ try {
641
+ const channel = new MessageChannel();
642
+ const client = await clientsRegistry.getClient(key);
643
+ if (!client) {
644
+ throw HttpError.errorResourceGone(params);
645
+ }
646
+ const data = {
647
+ type: "http",
648
+ key,
649
+ };
650
+ if (!await callChannel(client, "CONNECT", data, channel.port2)) {
651
+ throw HttpError.errorForbidden(params);
652
+ }
653
+ return await sendHttpRequest(channel.port1, request);
654
+ } catch (error) {
655
+ error = HttpError.fromError(error);
656
+ const options = error.getResponseOptions(params);
657
+ return new Response(JSON.stringify(options), {
658
+ ...options,
659
+ headers: {
660
+ "Content-Type": "text/javascript",
661
+ },
662
+ });
663
+ }
664
+ };
665
+ event.respondWith(handler());
561
666
  }
562
- const index = new Map();
563
- for (let clientId of clientIds) {
564
- const client = await this.self.clients.get(clientId);
565
- if (client) index.set(clientId, client);
566
- else this.handlersIndex.delete(clientId);
667
+ };
668
+ self.addEventListener("fetch", fetchListener);
669
+ register(() => self.removeEventListener("fetch", fetchListener));
670
+
671
+ return clear;
672
+ }
673
+
674
+ // --------------------------------------------
675
+ // errors.js
676
+
677
+ // --------------------------------------------
678
+
679
+ function newClientsRegistry({
680
+ self,
681
+ key = "clientsIds",
682
+ get,
683
+ set,
684
+ }) {
685
+ let _index;
686
+ async function loadClientsIndex() {
687
+ if (!_index) {
688
+ const entries = (await get(key)) || [];
689
+ _index = Object.fromEntries(entries);
567
690
  }
568
- await set("clientIds", [...index.keys()].sort());
691
+ return _index;
692
+ }
693
+ async function storeClientsIndex() {
694
+ const index = await loadClientsIndex();
695
+ const entries = Object.entries(index);
696
+ await set(key, entries);
569
697
  return index;
570
698
  }
571
- }
572
699
 
573
- class SwHttpDispatcher extends SwPortDispatcher {
574
- constructor(options) {
575
- super(options);
700
+ async function addClient(clientKey, client) {
701
+ const index = await loadClientsIndex();
702
+ const clientId = client.id;
703
+ let result = false;
704
+ if (!(clientId in index)) {
705
+ index[clientKey] = clientId;
706
+ await storeClientsIndex();
707
+ result = true;
708
+ }
709
+ return result;
576
710
  }
577
711
 
578
- start() {
579
- super.start();
580
- this.self.addEventListener("fetch", this._handleFetchEvent.bind(this));
712
+ async function removeClient(clientKey) {
713
+ const index = await loadClientsIndex();
714
+ let result = false;
715
+ if (clientKey in index) {
716
+ delete index[clientKey];
717
+ await storeClientsIndex();
718
+ result = true;
719
+ }
720
+ return result;
581
721
  }
582
722
 
583
- _handleFetchEvent(event) {
584
- event.respondWith((async () => {
585
- const request = event.request;
586
- try {
587
- const requestUrl = request.url;
588
- const rootUrl = this.scope;
589
- let channelInfo;
590
- if (requestUrl.indexOf(rootUrl) === 0) {
591
- const key = requestUrl
592
- .substring(rootUrl.length)
593
- .replace(/^\/?([^\/]+).*$/, "$1");
594
- // console.log(`* rootUrl="${rootUrl}" requestUrl="${requestUrl}" key="${key}"`);
595
- channelInfo = await this.loadChannelInfo(key);
596
- }
597
- if (channelInfo && channelInfo.port) {
598
- return await sendHttpRequest(channelInfo.port, request);
599
- } else {
600
- const newRequest = new Request(requestUrl, {
601
- method: request.method,
602
- headers: request.headers,
603
- body: request.body,
604
- referrer: request.referrer,
605
- referrerPolicy: request.referrerPolicy,
606
- // mode: request.mode,
607
- credentials: request.credentials,
608
- cache: request.cache,
609
- redirect: request.redirect,
610
- integrity: request.integrity,
611
- });
612
- return await fetch(newRequest);
613
- }
614
-
615
- // const requestUrl = request.url;
616
- // let url = requestUrl.replace(
617
- // /^.*\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}(.*)$/,
618
- // "$1",
619
- // );
620
- // if (url !== requestUrl) {
621
- // url = new URL(
622
- // "/packages/webrun-http-browser/src" + url,
623
- // self.location.href,
624
- // );
625
- // }
626
- } catch (error) {
627
- console.error(error);
628
- return new Response(null, {
629
- status: 500,
630
- statusText: "Error 500: Internal error",
631
- });
632
- }
633
- })());
723
+ async function getClient(clientKey) {
724
+ const index = await loadClientsIndex();
725
+ const clientId = index[clientKey];
726
+ const client = await self.clients.get(clientId);
727
+ if (!client) {
728
+ delete index[clientKey];
729
+ await storeClientsIndex();
730
+ }
731
+ return client;
634
732
  }
635
- }
636
- function startHttpDispatcher({ self, log }) {
637
- const dispatcher = new SwHttpDispatcher({ self, log });
638
- dispatcher.start();
639
- return () => {
640
- dispatcher.stop();
733
+
734
+ return {
735
+ getClient,
736
+ addClient,
737
+ removeClient,
641
738
  };
642
739
  }
643
-
644
- const result = startHttpDispatcher({
645
- self,
646
- log: console.log.bind(console, "[sw]"),
647
- });
648
-
649
- exports.result = result;
740
+ // ----------------------------------------------------
741
+ // Content of the "idb-keyval" package
742
+ // import { get, set } from "idb-keyval";
743
+
744
+ // function promisifyRequest(request) {
745
+ // return new Promise((resolve, reject) => {
746
+ // // @ts-ignore - file size hacks
747
+ // request.oncomplete = request.onsuccess = () => resolve(request.result);
748
+ // // @ts-ignore - file size hacks
749
+ // request.onabort = request.onerror = () => reject(request.error);
750
+ // });
751
+ // }
752
+ // function createStore(dbName, storeName) {
753
+ // const request = indexedDB.open(dbName);
754
+ // request.onupgradeneeded = () => request.result.createObjectStore(storeName);
755
+ // const dbp = promisifyRequest(request);
756
+ // return (txMode, callback) =>
757
+ // dbp.then((db) =>
758
+ // callback(db.transaction(storeName, txMode).objectStore(storeName))
759
+ // );
760
+ // }
761
+ // let defaultGetStoreFunc;
762
+ // function defaultGetStore() {
763
+ // if (!defaultGetStoreFunc) {
764
+ // defaultGetStoreFunc = createStore("keyval-store", "keyval");
765
+ // }
766
+ // return defaultGetStoreFunc;
767
+ // }
768
+ // /**
769
+ // * Get a value by its key.
770
+ // *
771
+ // * @param key
772
+ // * @param customStore Method to get a custom store. Use with caution (see the docs).
773
+ // */
774
+ // function get(key, customStore = defaultGetStore()) {
775
+ // return customStore("readonly", (store) => promisifyRequest(store.get(key)));
776
+ // }
777
+ // /**
778
+ // * Set a value with a key.
779
+ // *
780
+ // * @param key
781
+ // * @param value
782
+ // * @param customStore Method to get a custom store. Use with caution (see the docs).
783
+ // */
784
+ // function set(key, value, customStore = defaultGetStore()) {
785
+ // return customStore("readwrite", (store) => {
786
+ // store.put(value, key);
787
+ // return promisifyRequest(store.transaction);
788
+ // });
789
+ // }
790
+
791
+ startRelayServiceWorker(self);
792
+ //
650
793
 
651
794
  }));