@enbox/browser 0.3.78 → 0.3.80
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 +42 -95
- package/dist/browser.mjs +14 -14
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/prepare-browser.js +93 -0
- package/dist/esm/prepare-browser.js.map +1 -0
- package/dist/esm/ui/connect-modal.js +4 -4
- package/dist/esm/ui/connect-modal.js.map +1 -1
- package/dist/esm/web-features.js +15 -7
- package/dist/esm/web-features.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/prepare-browser.d.ts +23 -0
- package/dist/types/prepare-browser.d.ts.map +1 -0
- package/dist/types/web-features.d.ts +8 -1
- package/dist/types/web-features.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/index.ts +2 -0
- package/src/prepare-browser.ts +91 -0
- package/src/ui/connect-modal.ts +4 -4
- package/src/web-features.ts +25 -8
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ActivatePolyfillsOptions } from './web-features.js';
|
|
2
|
+
|
|
3
|
+
import { activatePolyfills } from './web-features.js';
|
|
4
|
+
|
|
5
|
+
/** Page startup options; worker registration and update policy remain application-owned. */
|
|
6
|
+
export type PrepareBrowserOptions = {
|
|
7
|
+
/** Forwarded to register() only when the worker is a script URL. */
|
|
8
|
+
registrationOptions?: RegistrationOptions;
|
|
9
|
+
/** Abort this wait without unregistering or cancelling a native registration. */
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
/** Bounds registration and control together. Defaults to 20 seconds. */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** Page polyfills, activated after control with serviceWorker forced to false. */
|
|
14
|
+
polyfills?: Omit<ActivatePolyfillsOptions, 'path' | 'serviceWorker' | 'onCacheCheck'>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Await an application worker controlling this page, then activate page polyfills.
|
|
19
|
+
* Existing registrations use their currently active worker, even when an update
|
|
20
|
+
* waits. Script URLs additionally require the controller's script URL to match.
|
|
21
|
+
* Obtain registrations from this page's navigator.serviceWorker; worker object
|
|
22
|
+
* identity is specific to each window, so another window's registration cannot
|
|
23
|
+
* be compared to this page's controller.
|
|
24
|
+
* This helper never forces worker takeover, reloads, or unregisters a worker.
|
|
25
|
+
*/
|
|
26
|
+
export async function prepareBrowser(
|
|
27
|
+
worker: string | URL | ServiceWorkerRegistration,
|
|
28
|
+
options: PrepareBrowserOptions = {},
|
|
29
|
+
): Promise<ServiceWorkerRegistration> {
|
|
30
|
+
const { signal, timeoutMs = 20_000 } = options;
|
|
31
|
+
signal?.throwIfAborted();
|
|
32
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647) {
|
|
33
|
+
throw new RangeError('prepareBrowser: timeoutMs must be between 0 and 2147483647 milliseconds.');
|
|
34
|
+
}
|
|
35
|
+
if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {
|
|
36
|
+
throw new Error('prepareBrowser: service workers are unavailable in this page.');
|
|
37
|
+
}
|
|
38
|
+
const container = navigator.serviceWorker;
|
|
39
|
+
const script = typeof worker === 'string' || 'href' in worker ? new URL(worker, document.baseURI) : undefined;
|
|
40
|
+
if (script !== undefined) {
|
|
41
|
+
script.hash = '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const registration = await new Promise<ServiceWorkerRegistration>((resolve, reject): void => {
|
|
45
|
+
let target: ServiceWorkerRegistration | undefined;
|
|
46
|
+
let settled = false;
|
|
47
|
+
const cleanup = (): void => {
|
|
48
|
+
settled = true;
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
container.removeEventListener('controllerchange', check);
|
|
51
|
+
signal?.removeEventListener('abort', abort);
|
|
52
|
+
};
|
|
53
|
+
const fail = (cause: unknown): void => {
|
|
54
|
+
if (settled) { return; }
|
|
55
|
+
cleanup();
|
|
56
|
+
reject(cause);
|
|
57
|
+
};
|
|
58
|
+
const check = (): void => {
|
|
59
|
+
const controller = container.controller;
|
|
60
|
+
if (settled || target === undefined || controller === null || controller !== target.active) { return; }
|
|
61
|
+
if (script !== undefined && controller.scriptURL !== script.href) { return; }
|
|
62
|
+
cleanup();
|
|
63
|
+
resolve(target);
|
|
64
|
+
};
|
|
65
|
+
const abort = (): void => fail(signal?.reason);
|
|
66
|
+
const timer = setTimeout((): void => {
|
|
67
|
+
fail(new DOMException('The application service worker did not take control before the timeout.', 'TimeoutError'));
|
|
68
|
+
}, timeoutMs);
|
|
69
|
+
|
|
70
|
+
container.addEventListener('controllerchange', check);
|
|
71
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
72
|
+
try {
|
|
73
|
+
signal?.throwIfAborted();
|
|
74
|
+
if (script === undefined) {
|
|
75
|
+
target = worker as ServiceWorkerRegistration;
|
|
76
|
+
check();
|
|
77
|
+
} else {
|
|
78
|
+
void container.register(script.href, options.registrationOptions).then((registered): void => {
|
|
79
|
+
if (settled) { return; }
|
|
80
|
+
target = registered;
|
|
81
|
+
check();
|
|
82
|
+
}, fail);
|
|
83
|
+
}
|
|
84
|
+
} catch (cause: unknown) {
|
|
85
|
+
fail(cause);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
signal?.throwIfAborted();
|
|
89
|
+
activatePolyfills({ ...options.polyfills, serviceWorker: false });
|
|
90
|
+
return registration;
|
|
91
|
+
}
|
package/src/ui/connect-modal.ts
CHANGED
|
@@ -856,7 +856,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
856
856
|
setStage(
|
|
857
857
|
el('p', 'stage-caption', 'This wallet can’t connect by phone here.'),
|
|
858
858
|
el('p', 'stage-subline', 'You can continue in this browser instead.'),
|
|
859
|
-
stageButton('Use this browser', () => { startPopup(); }),
|
|
859
|
+
stageButton('Use this browser', () => { void startPopup(); }),
|
|
860
860
|
);
|
|
861
861
|
};
|
|
862
862
|
|
|
@@ -882,7 +882,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
882
882
|
? `Reconnect with ${walletName}`
|
|
883
883
|
: 'Connect with a wallet in this browser'),
|
|
884
884
|
el('p', 'stage-subline', description),
|
|
885
|
-
stageButton(buttonLabel, () => { startPopup(); }),
|
|
885
|
+
stageButton(buttonLabel, () => { void startPopup(); }),
|
|
886
886
|
);
|
|
887
887
|
};
|
|
888
888
|
|
|
@@ -899,7 +899,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
899
899
|
el('p', 'stage-caption', blocked
|
|
900
900
|
? `Your browser blocked the wallet ${popupSurface}.`
|
|
901
901
|
: `The wallet ${popupSurface} was closed.`),
|
|
902
|
-
stageButton(blocked ? 'Open it now' : `Reopen ${popupSurface}`, () => { startPopup(); }),
|
|
902
|
+
stageButton(blocked ? 'Open it now' : `Reopen ${popupSurface}`, () => { void startPopup(); }),
|
|
903
903
|
...(savedRoute !== undefined
|
|
904
904
|
? [stageLinkButton(savedRouteEscapeLabel, escapeSavedRoute)]
|
|
905
905
|
: [stageLinkButton(isMobile ? 'Use a code instead' : 'Use your phone instead', () => { void switchMethod('phone'); })]),
|
|
@@ -1124,7 +1124,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
1124
1124
|
// popup channel actually buys the visitor is skipping the code.
|
|
1125
1125
|
alt.textContent = isMobile ? 'Or connect without a code →' : 'No phone? Use this browser →';
|
|
1126
1126
|
// startPopup runs synchronously in this click handler.
|
|
1127
|
-
alt.addEventListener('click', () => { startPopup(); });
|
|
1127
|
+
alt.addEventListener('click', () => { void startPopup(); });
|
|
1128
1128
|
row.appendChild(alt);
|
|
1129
1129
|
} else if (method === 'browser' && savedRoute === undefined) {
|
|
1130
1130
|
const alt = document.createElement('button');
|
package/src/web-features.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
so take care to gate additions to only activate code in the right env, such as a Service Worker scope or page window.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { DidMethodResolver } from '@enbox/dids';
|
|
7
|
+
import type { DidDhtNetworkConfig, DidMethodResolver } from '@enbox/dids';
|
|
8
8
|
|
|
9
|
-
import { DidDht, DidResolverCacheMemory, DidWeb, resolveDwnEndpointStatus, UniversalResolver } from '@enbox/dids';
|
|
9
|
+
import { createDidDhtMethod, DidDht, DidResolverCacheMemory, DidWeb, resolveDwnEndpointStatus, UniversalResolver } from '@enbox/dids';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Result returned by the `onCacheCheck` callback.
|
|
@@ -51,6 +51,13 @@ export type ActivatePolyfillsOptions = {
|
|
|
51
51
|
* Pass a custom array to add or replace DID methods (e.g., `[DidDht, DidWeb, DidJwk]`).
|
|
52
52
|
*/
|
|
53
53
|
didResolvers?: DidMethodResolver[];
|
|
54
|
+
/**
|
|
55
|
+
* Configure the realm-wide default `did:dht` resolver for this page or worker.
|
|
56
|
+
* Cannot be combined with `didResolvers`. Call `activatePolyfills()` in each service worker
|
|
57
|
+
* with the same actor-scoped bootstrap configuration used by its page. A later call in the same
|
|
58
|
+
* realm replaces the resolver and cache for that realm.
|
|
59
|
+
*/
|
|
60
|
+
didDhtNetwork?: DidDhtNetworkConfig;
|
|
54
61
|
};
|
|
55
62
|
|
|
56
63
|
/**
|
|
@@ -67,6 +74,21 @@ let didResolver = new UniversalResolver({
|
|
|
67
74
|
cache : new DidResolverCacheMemory(),
|
|
68
75
|
});
|
|
69
76
|
|
|
77
|
+
function configureDidResolver({ didDhtNetwork, didResolvers }: ActivatePolyfillsOptions): void {
|
|
78
|
+
if (didResolvers !== undefined && didDhtNetwork !== undefined) {
|
|
79
|
+
throw new Error('activatePolyfills: didDhtNetwork cannot be combined with didResolvers. Configure the resolver list directly.');
|
|
80
|
+
}
|
|
81
|
+
if (didResolvers === undefined && didDhtNetwork === undefined) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const didDht = didDhtNetwork === undefined ? DidDht : createDidDhtMethod(didDhtNetwork);
|
|
86
|
+
didResolver = new UniversalResolver({
|
|
87
|
+
didResolvers : didResolvers ?? [didDht, DidWeb],
|
|
88
|
+
cache : new DidResolverCacheMemory(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
import { parseDrlUrl } from './drl-url-parser.js';
|
|
71
93
|
export { parseDrlUrl } from './drl-url-parser.js';
|
|
72
94
|
|
|
@@ -581,12 +603,7 @@ async function resetContextMenuTarget(e?: Event): Promise<void> {
|
|
|
581
603
|
* activatePolyfills({ didResolvers: [DidDht, DidWeb, DidJwk] });
|
|
582
604
|
*/
|
|
583
605
|
export function activatePolyfills(options: ActivatePolyfillsOptions = {}): void {
|
|
584
|
-
|
|
585
|
-
didResolver = new UniversalResolver({
|
|
586
|
-
didResolvers : options.didResolvers,
|
|
587
|
-
cache : new DidResolverCacheMemory(),
|
|
588
|
-
});
|
|
589
|
-
}
|
|
606
|
+
configureDidResolver(options);
|
|
590
607
|
if (options.serviceWorker !== false) {
|
|
591
608
|
installWorker(options);
|
|
592
609
|
}
|