@enbox/browser 0.0.1

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,61 @@
1
+ # Web5 Browser package
2
+
3
+ | Web5 tools and features to use in the browser |
4
+ | --------------------------------------------- |
5
+
6
+ [![NPM Package][browser-npm-badge]][browser-npm-link]
7
+ [![NPM Downloads][browser-downloads-badge]][browser-npm-link]
8
+
9
+ [![Build Status][browser-build-badge]][browser-build-link]
10
+ [![Open Issues][browser-issues-badge]][browser-issues-link]
11
+ [![Code Coverage][browser-coverage-badge]][browser-coverage-link]
12
+
13
+ ---
14
+
15
+ - [Web5 Browser](#introduction)
16
+ - [Activate Polyfills](#activate-polyfills)
17
+ - [Project Resources](#project-resources)
18
+
19
+ ---
20
+
21
+ <a id="introduction"></a>
22
+
23
+ This package contains browser-specific helpers for building DWAs (Decentralized Web Apps).
24
+
25
+ ### Activate Polyfills
26
+
27
+ This enables a service worker that can handle Web5 features in the browser such as resolving DRLs that look like: `http://dweb/did:dht:abc123/protocols/read/aHR0cHM6Ly9hcmV3ZXdlYjV5ZXQuY29tL3NjaGVtYXMvcHJvdG9jb2xz/avatar`
28
+
29
+ To enable this functionality import and run `activatePolyfills()` at the entrypoint of your project, or within an existing service worker.
30
+
31
+ ## Project Resources
32
+
33
+ | Resource | Description |
34
+ | --------------------------------------- | ----------------------------------------------------------------------------- |
35
+ | [CODEOWNERS][codeowners-link] | Outlines the project lead(s) |
36
+ | [CODE OF CONDUCT][code-of-conduct-link] | Expected behavior for project contributors, promoting a welcoming environment |
37
+ | [CONTRIBUTING][contributing-link] | Developer guide to build, test, run, access CI, chat, discuss, file issues |
38
+ | [GOVERNANCE][governance-link] | Project governance |
39
+ | [LICENSE][license-link] | Apache License, Version 2.0 |
40
+
41
+ [browser-npm-badge]: https://img.shields.io/npm/v/@enbox/browser.svg?style=flat&color=blue&santize=true
42
+ [browser-npm-link]: https://www.npmjs.com/package/@enbox/browser
43
+ [browser-downloads-badge]: https://img.shields.io/npm/dt/@enbox/browser?&color=blue
44
+ [browser-build-badge]: https://img.shields.io/github/actions/workflow/status/enboxorg/enbox/enbox/tests-ci.yml?branch=main&label=build
45
+ [browser-build-link]: https://github.com/enboxorg/enbox/enbox/actions/workflows/tests-ci.yml
46
+ [browser-coverage-badge]: https://img.shields.io/codecov/c/gh/enboxorg/enbox/enbox/main?style=flat&token=YI87CKF1LI
47
+ [browser-coverage-link]: https://app.codecov.io/github/enboxorg/enbox/enbox/tree/main/packages%2Fbrowser
48
+ [browser-issues-badge]: https://img.shields.io/github/issues/enboxorg/enbox/enbox/package:%20browser?label=issues
49
+ [browser-issues-link]: https://github.com/enboxorg/enbox/enbox/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+browser"
50
+ [browser-repo-link]: https://github.com/enboxorg/enbox/enbox/tree/main/packages/browser
51
+ [browser-jsdelivr-link]: https://www.jsdelivr.com/package/npm/@enbox/browser
52
+ [browser-jsdelivr-browser]: https://cdn.jsdelivr.net/npm/@enbox/browser/dist/browser.mjs
53
+ [browser-unpkg-link]: https://unpkg.com/@enbox/browser
54
+ [browser-unpkg-browser]: https://unpkg.com/@enbox/browser/dist/browser.mjs
55
+ [codeowners-link]: https://github.com/enboxorg/enbox/enbox/blob/main/CODEOWNERS
56
+ [code-of-conduct-link]: https://github.com/enboxorg/enbox/enbox/blob/main/CODE_OF_CONDUCT.md
57
+ [contributing-link]: https://github.com/enboxorg/enbox/enbox/blob/main/CONTRIBUTING.md
58
+ [governance-link]: https://github.com/enboxorg/enbox/enbox/blob/main/GOVERNANCE.md
59
+ [license-link]: https://github.com/enboxorg/enbox/enbox/blob/main/LICENSE
60
+ [discord-badge]: https://img.shields.io/discord/937858703112155166?color=5865F2&logo=discord&logoColor=white
61
+ [discord-link]: https://discord.com/channels/937858703112155166/969272658501976117
@@ -0,0 +1,2 @@
1
+ export * from './web-features.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,481 @@
1
+ //@ts-nocheck
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ /*
12
+ This file is run in dual environments to make installation of the Service Worker code easier.
13
+ Be mindful that code placed in any open excution space may be evaluated multiple times in different contexts,
14
+ so take care to gate additions to only activate code in the right env, such as a Service Worker scope or page window.
15
+ */
16
+ import { UniversalResolver, DidDht, DidWeb } from '@enbox/dids';
17
+ // This is in place to prevent our `bundler-bonanza` repo from failing for Node CJS builds
18
+ // Not sure if this is working as expected in all environments, crated an issue
19
+ // TODO: https://github.com/TBD54566975/web5-js/issues/767
20
+ function importMetaIfSupported() {
21
+ try {
22
+ return new Function('return import.meta')();
23
+ }
24
+ catch (_error) {
25
+ return undefined;
26
+ }
27
+ }
28
+ const DidResolver = new UniversalResolver({ didResolvers: [DidDht, DidWeb] });
29
+ const didUrlRegex = /^https?:\/\/dweb\/([^/]+)\/?(.*)?$/;
30
+ const httpToHttpsRegex = /^http:/;
31
+ const trailingSlashRegex = /\/$/;
32
+ function getDwnEndpoints(did) {
33
+ var _a, _b;
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ const { didDocument } = yield DidResolver.resolve(did);
36
+ const endpoints = (_b = (_a = didDocument === null || didDocument === void 0 ? void 0 : didDocument.service) === null || _a === void 0 ? void 0 : _a.find((service) => service.type === 'DecentralizedWebNode')) === null || _b === void 0 ? void 0 : _b.serviceEndpoint;
37
+ return (Array.isArray(endpoints) ? endpoints : [endpoints]).filter((url) => url.startsWith('http'));
38
+ });
39
+ }
40
+ function handleEvent(event, did, path, options) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ const drl = event.request.url
43
+ .replace(httpToHttpsRegex, 'https:')
44
+ .replace(trailingSlashRegex, '');
45
+ const responseCache = yield caches.open('drl');
46
+ const cachedResponse = yield responseCache.match(drl);
47
+ if (cachedResponse) {
48
+ if (!navigator.onLine)
49
+ return cachedResponse;
50
+ const match = yield (options === null || options === void 0 ? void 0 : options.onCacheCheck(event, drl));
51
+ if (match) {
52
+ const cacheTime = cachedResponse.headers.get('dwn-cache-time');
53
+ if (cacheTime &&
54
+ Date.now() < Number(cacheTime) + (Number(match.ttl) || 0)) {
55
+ return cachedResponse;
56
+ }
57
+ }
58
+ }
59
+ try {
60
+ if (!path) {
61
+ const response = yield DidResolver.resolve(did);
62
+ return new Response(JSON.stringify(response), {
63
+ status: 200,
64
+ headers: {
65
+ 'Content-Type': 'application/json',
66
+ },
67
+ });
68
+ }
69
+ else
70
+ return yield fetchResource(event, did, drl, path, responseCache, options);
71
+ }
72
+ catch (error) {
73
+ if (error instanceof Response) {
74
+ return error;
75
+ }
76
+ console.log(`Error in DID URL fetch: ${error}`);
77
+ return new Response('DID URL fetch error', { status: 500 });
78
+ }
79
+ });
80
+ }
81
+ function fetchResource(event, did, drl, path, responseCache, options) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ const endpoints = yield getDwnEndpoints(did);
84
+ if (!(endpoints === null || endpoints === void 0 ? void 0 : endpoints.length)) {
85
+ throw new Response('DWeb Node resolution failed: no valid endpoints found.', { status: 530 });
86
+ }
87
+ for (const endpoint of endpoints) {
88
+ try {
89
+ const url = `${endpoint.replace(trailingSlashRegex, '')}/${did}/${path}`;
90
+ const response = yield fetch(url, { headers: event.request.headers });
91
+ if (response.ok) {
92
+ const match = yield (options === null || options === void 0 ? void 0 : options.onCacheCheck(event, drl));
93
+ if (match) {
94
+ cacheResponse(drl, url, response, responseCache);
95
+ }
96
+ return response;
97
+ }
98
+ console.log(`DWN endpoint error: ${response.status}`);
99
+ return new Response('DWeb Node request failed', {
100
+ status: response.status,
101
+ });
102
+ }
103
+ catch (error) {
104
+ console.log(`DWN endpoint error: ${error}`);
105
+ return new Response('DWeb Node request failed: ' + error, {
106
+ status: 500,
107
+ });
108
+ }
109
+ }
110
+ });
111
+ }
112
+ function cacheResponse(drl, url, response, cache) {
113
+ return __awaiter(this, void 0, void 0, function* () {
114
+ const clonedResponse = response.clone();
115
+ const headers = new Headers(clonedResponse.headers);
116
+ headers.append('dwn-cache-time', Date.now().toString());
117
+ headers.append('dwn-composed-url', url);
118
+ const modifiedResponse = new Response(clonedResponse.body, { headers });
119
+ cache.put(drl, modifiedResponse);
120
+ });
121
+ }
122
+ /* Service Worker-based features */
123
+ function installWorker(options = {}) {
124
+ var _a, _b, _c;
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const workerSelf = self;
127
+ try {
128
+ // Check to see if we are in a Service Worker already, if so, proceed
129
+ // You can call the activatePolyfills() function in your own worker, or standalone as a root worker
130
+ if (typeof ServiceWorkerGlobalScope !== 'undefined' &&
131
+ workerSelf instanceof ServiceWorkerGlobalScope) {
132
+ workerSelf.skipWaiting();
133
+ workerSelf.addEventListener('activate', (event) => {
134
+ // Claim clients to make the service worker take control immediately
135
+ event.waitUntil(workerSelf.clients.claim());
136
+ });
137
+ workerSelf.addEventListener('fetch', (event) => {
138
+ const match = event.request.url.match(didUrlRegex);
139
+ if (match) {
140
+ event.respondWith(handleEvent(event, match[1], match[2], options));
141
+ }
142
+ });
143
+ }
144
+ // If the code gets here, it is not a SW env, it is likely DOM, but check to be sure
145
+ else if ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.navigator) === null || _a === void 0 ? void 0 : _a.serviceWorker) {
146
+ const registration = yield navigator.serviceWorker.getRegistration('/');
147
+ // You can only have one worker per path, so check to see if one is already registered
148
+ if (!registration) {
149
+ // @ts-ignore
150
+ const installUrl = options.path ||
151
+ (globalThis.document
152
+ ? (_b = document === null || document === void 0 ? void 0 : document.currentScript) === null || _b === void 0 ? void 0 : _b.src
153
+ : (_c = importMetaIfSupported()) === null || _c === void 0 ? void 0 : _c.url);
154
+ if (installUrl)
155
+ navigator.serviceWorker
156
+ .register(installUrl, { type: 'module' })
157
+ .catch((error) => {
158
+ console.error('DWeb networking feature installation failed: ', error);
159
+ });
160
+ }
161
+ }
162
+ else {
163
+ throw new Error('DWeb networking features are not available for install in this environment');
164
+ }
165
+ }
166
+ catch (error) {
167
+ console.error('Error in installing networking features:', error);
168
+ }
169
+ });
170
+ }
171
+ /* DOM Environment Features */
172
+ const loaderStyles = `
173
+ .drl-loading-overlay {
174
+ position: fixed;
175
+ inset: 0;
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ flex-direction: column;
179
+ align-items: center;
180
+ justify-content: center;
181
+ font-size: 22px;
182
+ color: #fff;
183
+ background: rgba(0, 0, 0, 0.75);
184
+ backdrop-filter: blur(15px);
185
+ -webkit-backdrop-filter: blur(15px);
186
+ z-index: 1000000;
187
+ }
188
+
189
+ .drl-loading-overlay > div {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ }
194
+
195
+ .drl-loading-spinner {
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: center;
199
+ }
200
+
201
+ .drl-loading-spinner div {
202
+ position: relative;
203
+ width: 2em;
204
+ height: 2em;
205
+ margin: 0.1em 0.25em 0 0;
206
+ }
207
+ .drl-loading-spinner div::after,
208
+ .drl-loading-spinner div::before {
209
+ content: '';
210
+ box-sizing: border-box;
211
+ width: 100%;
212
+ height: 100%;
213
+ border-radius: 50%;
214
+ border: 0.1em solid #FFF;
215
+ position: absolute;
216
+ left: 0;
217
+ top: 0;
218
+ opacity: 0;
219
+ animation: drl-loading-spinner 2s linear infinite;
220
+ }
221
+ .drl-loading-spinner div::after {
222
+ animation-delay: 1s;
223
+ }
224
+
225
+ .drl-loading-overlay span {
226
+ --text-opacity: 2;
227
+ display: flex;
228
+ align-items: center;
229
+ margin: 2em auto 0;
230
+ padding: 0.2em 0.75em 0.25em;
231
+ text-align: center;
232
+ border-radius: 5em;
233
+ background: rgba(255 255 255 / 8%);
234
+ opacity: 0.8;
235
+ transition: opacity 0.3s ease;
236
+ cursor: pointer;
237
+ }
238
+
239
+ .drl-loading-overlay span:focus {
240
+ opacity: 1;
241
+ }
242
+
243
+ .drl-loading-overlay span:hover {
244
+ opacity: 1;
245
+ }
246
+
247
+ .drl-loading-overlay span::before {
248
+ content: '✕ ';
249
+ margin: 0 0.4em 0 0;
250
+ color: red;
251
+ font-size: 65%;
252
+ font-weight: bold;
253
+ }
254
+
255
+ .drl-loading-overlay span::after {
256
+ content: 'stop';
257
+ display: block;
258
+ font-size: 60%;
259
+ line-height: 0;
260
+ color: rgba(255 255 255 / 60%);
261
+ }
262
+
263
+ .drl-loading-overlay.new-tab-overlay span::after {
264
+ content: 'close';
265
+ }
266
+
267
+ @keyframes drl-loading-spinner {
268
+ 0% {
269
+ transform: scale(0);
270
+ opacity: 1;
271
+ }
272
+ 100% {
273
+ transform: scale(1);
274
+ opacity: 0;
275
+ }
276
+ }
277
+ `;
278
+ const tabContent = `
279
+ <!DOCTYPE html>
280
+ <html lang='en'>
281
+ <head>
282
+ <meta charset='UTF-8'>
283
+ <meta name='viewport' content='width=device-width, initial-scale=1.0'>
284
+ <title>Loading DRL...</title>
285
+ <style>
286
+ html, body {
287
+ background-color: #151518;
288
+ height: 100%;
289
+ margin: 0;
290
+ padding: 0;
291
+ font-family: Arial, sans-serif;
292
+ text-align: center;
293
+ }
294
+ ${loaderStyles}
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <div class='drl-loading-overlay new-tab-overlay'>
299
+ <div class='drl-loading-spinner'>
300
+ <div></div>
301
+ Loading DRL
302
+ </div>
303
+ <span onclick='window.close()' tabindex='0'></span>
304
+ </div>
305
+ </body>
306
+ </html>
307
+ `;
308
+ let elementsInjected = false;
309
+ function injectElements() {
310
+ if (elementsInjected)
311
+ return;
312
+ const style = document.createElement('style');
313
+ style.innerHTML = `
314
+ ${loaderStyles}
315
+
316
+ .drl-loading-overlay {
317
+ opacity: 0;
318
+ transition: opacity 0.3s ease;
319
+ pointer-events: none;
320
+ }
321
+
322
+ :root[drl-link-loading] .drl-loading-overlay {
323
+ opacity: 1;
324
+ pointer-events: all;
325
+ }
326
+ `;
327
+ document.head.append(style);
328
+ const overlay = document.createElement('div');
329
+ overlay.classList.add('drl-loading-overlay');
330
+ overlay.innerHTML = `
331
+ <div class='drl-loading-spinner'>
332
+ <div></div>
333
+ Loading DRL
334
+ </div>
335
+ <span tabindex='0'></span>
336
+ `;
337
+ overlay.lastElementChild.addEventListener('click', cancelNavigation);
338
+ document.body.prepend(overlay);
339
+ elementsInjected = true;
340
+ }
341
+ function cancelNavigation() {
342
+ document.documentElement.removeAttribute('drl-link-loading');
343
+ activeNavigation = null;
344
+ }
345
+ let activeNavigation;
346
+ let linkFeaturesActive = false;
347
+ function addLinkFeatures() {
348
+ if (!linkFeaturesActive) {
349
+ document.addEventListener('click', (event) => __awaiter(this, void 0, void 0, function* () {
350
+ const anchor = event.target.closest('a');
351
+ if (anchor) {
352
+ const href = anchor.href;
353
+ const match = href.match(didUrlRegex);
354
+ if (match) {
355
+ const did = match[1];
356
+ const path = match[2];
357
+ const openAsTab = anchor.target === '_blank';
358
+ event.preventDefault();
359
+ try {
360
+ let tab;
361
+ if (openAsTab) {
362
+ tab = window.open('', '_blank');
363
+ tab.document.write(tabContent);
364
+ }
365
+ else {
366
+ activeNavigation = path;
367
+ // this is to allow for cached DIDs to instantly load without any flash of loading UI
368
+ setTimeout(() => document.documentElement.setAttribute('drl-link-loading', ''), 50);
369
+ }
370
+ const endpoints = yield getDwnEndpoints(did);
371
+ if (!endpoints.length)
372
+ throw null;
373
+ const url = `${endpoints[0].replace(trailingSlashRegex, '')}/${did}/${path}`;
374
+ if (openAsTab) {
375
+ if (!tab.closed)
376
+ tab.location.href = url;
377
+ }
378
+ else if (activeNavigation === path) {
379
+ window.location.href = url;
380
+ }
381
+ }
382
+ catch (e) {
383
+ if (activeNavigation === path) {
384
+ cancelNavigation();
385
+ }
386
+ throw new Error(`DID endpoint resolution failed for the DRL: ${href}`);
387
+ }
388
+ }
389
+ }
390
+ }));
391
+ document.addEventListener('pointercancel', resetContextMenuTarget);
392
+ document.addEventListener('pointerdown', (event) => __awaiter(this, void 0, void 0, function* () {
393
+ var _a;
394
+ const target = event.composedPath()[0];
395
+ if ((event.pointerType === 'mouse' && event.button === 2) ||
396
+ (event.pointerType === 'touch' && event.isPrimary)) {
397
+ resetContextMenuTarget();
398
+ if (target && ((_a = target === null || target === void 0 ? void 0 : target.src) === null || _a === void 0 ? void 0 : _a.match(didUrlRegex))) {
399
+ contextMenuTarget = target;
400
+ target.__src__ = target.src;
401
+ const drl = target.src
402
+ .replace(httpToHttpsRegex, 'https:')
403
+ .replace(trailingSlashRegex, '');
404
+ const responseCache = yield caches.open('drl');
405
+ const response = yield responseCache.match(drl);
406
+ const url = response.headers.get('dwn-composed-url');
407
+ if (url)
408
+ target.src = url;
409
+ target.addEventListener('pointerup', resetContextMenuTarget, {
410
+ once: true,
411
+ });
412
+ }
413
+ }
414
+ else if (target === contextMenuTarget) {
415
+ resetContextMenuTarget();
416
+ }
417
+ }));
418
+ linkFeaturesActive = true;
419
+ }
420
+ }
421
+ let contextMenuTarget;
422
+ function resetContextMenuTarget(e) {
423
+ return __awaiter(this, void 0, void 0, function* () {
424
+ if ((e === null || e === void 0 ? void 0 : e.type) === 'pointerup') {
425
+ yield new Promise((r) => requestAnimationFrame(r));
426
+ }
427
+ if (contextMenuTarget) {
428
+ contextMenuTarget.src = contextMenuTarget.__src__;
429
+ delete contextMenuTarget.__src__;
430
+ contextMenuTarget = null;
431
+ }
432
+ });
433
+ }
434
+ /**
435
+ * Activates various polyfills to enable Web5 features in Web environments.
436
+ *
437
+ * @param {object} [options={}] - Configuration options to control the activation of polyfills.
438
+ * @param {boolean} [options.serviceWorker=true] - Option to avoid installation of the Service Worker. Defaults to true, installing the Service Worker.
439
+ * @param {boolean} [options.injectStyles=true] - Option to skip injection of styles for UI related UX polyfills. Defaults to true, injecting styles.
440
+ * @param {boolean} [options.links=true] - Option to skip activation of DRL link features. Defaults to true, activating link features.
441
+ * @param {function} [options.onCacheCheck] - Callback function to handle cache check events, allowing fine-grained control over what DRL request to cache, and for how long.
442
+ * @param {object} [options.onCacheCheck.event] - The event object passed to the callback.
443
+ * @param {object} [options.onCacheCheck.route] - The route object passed to the callback.
444
+ * @returns {object} [options.onCacheCheck.return] - The return object from the callback.
445
+ * @returns {number} [options.onCacheCheck.return.ttl] - Time-to-live for the cached DRL response, in milliseconds.
446
+ *
447
+ * @returns {void}
448
+ *
449
+ * @example
450
+ * // Activate all polyfills with default options, and cache every DRL for 1 minute
451
+ * activatePolyfills({
452
+ * onCacheCheck(event, route){
453
+ * return {
454
+ * ttl: 60_000
455
+ * }
456
+ * }
457
+ * });
458
+ *
459
+ * @example
460
+ * // Activate polyfills, but without Service Worker activation
461
+ * activatePolyfills({ serviceWorker: false });
462
+ */
463
+ export function activatePolyfills(options = {}) {
464
+ if (options.serviceWorker !== false) {
465
+ installWorker(options);
466
+ }
467
+ if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
468
+ if (options.injectStyles !== false) {
469
+ if (document.readyState !== 'loading')
470
+ injectElements();
471
+ else {
472
+ document.addEventListener('DOMContentLoaded', injectElements, {
473
+ once: true,
474
+ });
475
+ }
476
+ }
477
+ if (options.links !== false)
478
+ addLinkFeatures();
479
+ }
480
+ }
481
+ //# sourceMappingURL=web-features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-features.js","sourceRoot":"","sources":["../../src/web-features.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './web-features.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Activates various polyfills to enable Web5 features in Web environments.
3
+ *
4
+ * @param {object} [options={}] - Configuration options to control the activation of polyfills.
5
+ * @param {boolean} [options.serviceWorker=true] - Option to avoid installation of the Service Worker. Defaults to true, installing the Service Worker.
6
+ * @param {boolean} [options.injectStyles=true] - Option to skip injection of styles for UI related UX polyfills. Defaults to true, injecting styles.
7
+ * @param {boolean} [options.links=true] - Option to skip activation of DRL link features. Defaults to true, activating link features.
8
+ * @param {function} [options.onCacheCheck] - Callback function to handle cache check events, allowing fine-grained control over what DRL request to cache, and for how long.
9
+ * @param {object} [options.onCacheCheck.event] - The event object passed to the callback.
10
+ * @param {object} [options.onCacheCheck.route] - The route object passed to the callback.
11
+ * @returns {object} [options.onCacheCheck.return] - The return object from the callback.
12
+ * @returns {number} [options.onCacheCheck.return.ttl] - Time-to-live for the cached DRL response, in milliseconds.
13
+ *
14
+ * @returns {void}
15
+ *
16
+ * @example
17
+ * // Activate all polyfills with default options, and cache every DRL for 1 minute
18
+ * activatePolyfills({
19
+ * onCacheCheck(event, route){
20
+ * return {
21
+ * ttl: 60_000
22
+ * }
23
+ * }
24
+ * });
25
+ *
26
+ * @example
27
+ * // Activate polyfills, but without Service Worker activation
28
+ * activatePolyfills({ serviceWorker: false });
29
+ */
30
+ export declare function activatePolyfills(options?: any): void;
31
+ //# sourceMappingURL=web-features.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-features.d.ts","sourceRoot":"","sources":["../../src/web-features.ts"],"names":[],"mappings":"AA4bA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,GAAQ,QAelD"}
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@enbox/browser",
3
+ "version": "0.0.1",
4
+ "description": "Web5 tools and features to use in the browser",
5
+ "type": "module",
6
+ "main": "./dist/esm/index.js",
7
+ "module": "./dist/esm/index.js",
8
+ "types": "./dist/types/index.d.ts",
9
+ "scripts": {
10
+ "clean": "rimraf dist coverage tests/compiled",
11
+ "build:tests": "rimraf tests/compiled && node build/esbuild-tests.cjs",
12
+ "build:esm": "rimraf dist/esm dist/types && pnpm tsc -p tsconfig.json",
13
+ "build:browser": "pnpm build:esm",
14
+ "build": "pnpm clean && pnpm build:esm",
15
+ "lint": "eslint . --max-warnings 0",
16
+ "lint:fix": "eslint . --fix",
17
+ "test:browser": "pnpm build:tests && web-test-runner"
18
+ },
19
+ "homepage": "https://github.com/enboxorg/enbox/tree/main/packages/browser#readme",
20
+ "bugs": "https://github.com/enboxorg/enbox/issues",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/enboxorg/enbox.git",
24
+ "directory": "packages/browser"
25
+ },
26
+ "license": "Apache-2.0",
27
+ "contributors": [
28
+ {
29
+ "name": "Daniel Buchner",
30
+ "url": "https://github.com/csuwildcat"
31
+ },
32
+ {
33
+ "name": "Liran Cohen",
34
+ "url": "https://github.com/lirancohen"
35
+ }
36
+ ],
37
+ "files": [
38
+ "dist",
39
+ "src"
40
+ ],
41
+ "keywords": [
42
+ "decentralized",
43
+ "decentralized-applications",
44
+ "decentralized-identity",
45
+ "decentralized-web",
46
+ "DID",
47
+ "sdk",
48
+ "verifiable-credentials",
49
+ "web5",
50
+ "web5-sdk",
51
+ "browser",
52
+ "tools"
53
+ ],
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "dependencies": {
58
+ "@enbox/dids": "workspace:*"
59
+ },
60
+ "devDependencies": {
61
+ "@playwright/test": "1.45.3",
62
+ "@types/chai": "4.3.6",
63
+ "@types/eslint": "8.56.10",
64
+ "@types/mocha": "10.0.1",
65
+ "@types/sinon": "17.0.3",
66
+ "@typescript-eslint/eslint-plugin": "7.9.0",
67
+ "@typescript-eslint/parser": "7.14.1",
68
+ "@web/test-runner": "0.18.2",
69
+ "@web/test-runner-playwright": "0.11.0",
70
+ "c8": "9.1.0",
71
+ "chai": "4.3.10",
72
+ "esbuild": "0.19.8",
73
+ "eslint": "9.3.0",
74
+ "eslint-plugin-mocha": "10.4.3",
75
+ "mocha": "10.2.0",
76
+ "mocha-junit-reporter": "2.2.1",
77
+ "playwright": "1.45.3",
78
+ "rimraf": "4.4.0",
79
+ "sinon": "18.0.0",
80
+ "source-map-loader": "4.0.2",
81
+ "typescript": "5.1.6"
82
+ }
83
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './web-features.js';
@@ -0,0 +1,489 @@
1
+ //@ts-nocheck
2
+
3
+ /*
4
+ This file is run in dual environments to make installation of the Service Worker code easier.
5
+ Be mindful that code placed in any open excution space may be evaluated multiple times in different contexts,
6
+ so take care to gate additions to only activate code in the right env, such as a Service Worker scope or page window.
7
+ */
8
+
9
+ import { UniversalResolver, DidDht, DidWeb } from '@enbox/dids';
10
+
11
+ // This is in place to prevent our `bundler-bonanza` repo from failing for Node CJS builds
12
+ // Not sure if this is working as expected in all environments, crated an issue
13
+ // TODO: https://github.com/TBD54566975/web5-js/issues/767
14
+ function importMetaIfSupported() {
15
+ try {
16
+ return new Function('return import.meta')();
17
+ } catch (_error) {
18
+ return undefined;
19
+ }
20
+ }
21
+
22
+ declare const ServiceWorkerGlobalScope: any;
23
+
24
+ const DidResolver = new UniversalResolver({ didResolvers: [DidDht, DidWeb] });
25
+ const didUrlRegex = /^https?:\/\/dweb\/([^/]+)\/?(.*)?$/;
26
+ const httpToHttpsRegex = /^http:/;
27
+ const trailingSlashRegex = /\/$/;
28
+
29
+ async function getDwnEndpoints(did) {
30
+ const { didDocument } = await DidResolver.resolve(did);
31
+ const endpoints = didDocument?.service?.find(
32
+ (service) => service.type === 'DecentralizedWebNode'
33
+ )?.serviceEndpoint;
34
+ return (Array.isArray(endpoints) ? endpoints : [endpoints]).filter((url) =>
35
+ url.startsWith('http')
36
+ );
37
+ }
38
+
39
+ async function handleEvent(event, did, path, options) {
40
+ const drl = event.request.url
41
+ .replace(httpToHttpsRegex, 'https:')
42
+ .replace(trailingSlashRegex, '');
43
+ const responseCache = await caches.open('drl');
44
+ const cachedResponse = await responseCache.match(drl);
45
+ if (cachedResponse) {
46
+ if (!navigator.onLine) return cachedResponse;
47
+ const match = await options?.onCacheCheck(event, drl);
48
+ if (match) {
49
+ const cacheTime = cachedResponse.headers.get('dwn-cache-time');
50
+ if (
51
+ cacheTime &&
52
+ Date.now() < Number(cacheTime) + (Number(match.ttl) || 0)
53
+ ) {
54
+ return cachedResponse;
55
+ }
56
+ }
57
+ }
58
+ try {
59
+ if (!path) {
60
+ const response = await DidResolver.resolve(did);
61
+ return new Response(JSON.stringify(response), {
62
+ status : 200,
63
+ headers : {
64
+ 'Content-Type': 'application/json',
65
+ },
66
+ });
67
+ } else
68
+ return await fetchResource(event, did, drl, path, responseCache, options);
69
+ } catch (error) {
70
+ if (error instanceof Response) {
71
+ return error;
72
+ }
73
+ console.log(`Error in DID URL fetch: ${error}`);
74
+ return new Response('DID URL fetch error', { status: 500 });
75
+ }
76
+ }
77
+
78
+ async function fetchResource(event, did, drl, path, responseCache, options) {
79
+ const endpoints = await getDwnEndpoints(did);
80
+ if (!endpoints?.length) {
81
+ throw new Response(
82
+ 'DWeb Node resolution failed: no valid endpoints found.',
83
+ { status: 530 }
84
+ );
85
+ }
86
+ for (const endpoint of endpoints) {
87
+ try {
88
+ const url = `${endpoint.replace(trailingSlashRegex, '')}/${did}/${path}`;
89
+ const response = await fetch(url, { headers: event.request.headers });
90
+ if (response.ok) {
91
+ const match = await options?.onCacheCheck(event, drl);
92
+ if (match) {
93
+ cacheResponse(drl, url, response, responseCache);
94
+ }
95
+ return response;
96
+ }
97
+ console.log(`DWN endpoint error: ${response.status}`);
98
+ return new Response('DWeb Node request failed', {
99
+ status: response.status,
100
+ });
101
+ } catch (error) {
102
+ console.log(`DWN endpoint error: ${error}`);
103
+ return new Response('DWeb Node request failed: ' + error, {
104
+ status: 500,
105
+ });
106
+ }
107
+ }
108
+ }
109
+
110
+ async function cacheResponse(drl, url, response, cache) {
111
+ const clonedResponse = response.clone();
112
+ const headers = new Headers(clonedResponse.headers);
113
+ headers.append('dwn-cache-time', Date.now().toString());
114
+ headers.append('dwn-composed-url', url);
115
+ const modifiedResponse = new Response(clonedResponse.body, { headers });
116
+ cache.put(drl, modifiedResponse);
117
+ }
118
+
119
+ /* Service Worker-based features */
120
+
121
+ async function installWorker(options: any = {}): Promise<void> {
122
+ const workerSelf = self as any;
123
+ try {
124
+ // Check to see if we are in a Service Worker already, if so, proceed
125
+ // You can call the activatePolyfills() function in your own worker, or standalone as a root worker
126
+ if (
127
+ typeof ServiceWorkerGlobalScope !== 'undefined' &&
128
+ workerSelf instanceof ServiceWorkerGlobalScope
129
+ ) {
130
+ workerSelf.skipWaiting();
131
+ workerSelf.addEventListener('activate', (event) => {
132
+ // Claim clients to make the service worker take control immediately
133
+ event.waitUntil(workerSelf.clients.claim());
134
+ });
135
+ workerSelf.addEventListener('fetch', (event) => {
136
+ const match = event.request.url.match(didUrlRegex);
137
+ if (match) {
138
+ event.respondWith(handleEvent(event, match[1], match[2], options));
139
+ }
140
+ });
141
+ }
142
+ // If the code gets here, it is not a SW env, it is likely DOM, but check to be sure
143
+ else if (globalThis?.navigator?.serviceWorker) {
144
+ const registration = await navigator.serviceWorker.getRegistration('/');
145
+ // You can only have one worker per path, so check to see if one is already registered
146
+ if (!registration) {
147
+ // @ts-ignore
148
+ const installUrl =
149
+ options.path ||
150
+ (globalThis.document
151
+ ? document?.currentScript?.src
152
+ : importMetaIfSupported()?.url);
153
+ if (installUrl)
154
+ navigator.serviceWorker
155
+ .register(installUrl, { type: 'module' })
156
+ .catch((error) => {
157
+ console.error(
158
+ 'DWeb networking feature installation failed: ',
159
+ error
160
+ );
161
+ });
162
+ }
163
+ } else {
164
+ throw new Error(
165
+ 'DWeb networking features are not available for install in this environment'
166
+ );
167
+ }
168
+ } catch (error) {
169
+ console.error('Error in installing networking features:', error);
170
+ }
171
+ }
172
+
173
+ /* DOM Environment Features */
174
+
175
+ const loaderStyles = `
176
+ .drl-loading-overlay {
177
+ position: fixed;
178
+ inset: 0;
179
+ display: flex;
180
+ flex-wrap: wrap;
181
+ flex-direction: column;
182
+ align-items: center;
183
+ justify-content: center;
184
+ font-size: 22px;
185
+ color: #fff;
186
+ background: rgba(0, 0, 0, 0.75);
187
+ backdrop-filter: blur(15px);
188
+ -webkit-backdrop-filter: blur(15px);
189
+ z-index: 1000000;
190
+ }
191
+
192
+ .drl-loading-overlay > div {
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ }
197
+
198
+ .drl-loading-spinner {
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ }
203
+
204
+ .drl-loading-spinner div {
205
+ position: relative;
206
+ width: 2em;
207
+ height: 2em;
208
+ margin: 0.1em 0.25em 0 0;
209
+ }
210
+ .drl-loading-spinner div::after,
211
+ .drl-loading-spinner div::before {
212
+ content: '';
213
+ box-sizing: border-box;
214
+ width: 100%;
215
+ height: 100%;
216
+ border-radius: 50%;
217
+ border: 0.1em solid #FFF;
218
+ position: absolute;
219
+ left: 0;
220
+ top: 0;
221
+ opacity: 0;
222
+ animation: drl-loading-spinner 2s linear infinite;
223
+ }
224
+ .drl-loading-spinner div::after {
225
+ animation-delay: 1s;
226
+ }
227
+
228
+ .drl-loading-overlay span {
229
+ --text-opacity: 2;
230
+ display: flex;
231
+ align-items: center;
232
+ margin: 2em auto 0;
233
+ padding: 0.2em 0.75em 0.25em;
234
+ text-align: center;
235
+ border-radius: 5em;
236
+ background: rgba(255 255 255 / 8%);
237
+ opacity: 0.8;
238
+ transition: opacity 0.3s ease;
239
+ cursor: pointer;
240
+ }
241
+
242
+ .drl-loading-overlay span:focus {
243
+ opacity: 1;
244
+ }
245
+
246
+ .drl-loading-overlay span:hover {
247
+ opacity: 1;
248
+ }
249
+
250
+ .drl-loading-overlay span::before {
251
+ content: '✕ ';
252
+ margin: 0 0.4em 0 0;
253
+ color: red;
254
+ font-size: 65%;
255
+ font-weight: bold;
256
+ }
257
+
258
+ .drl-loading-overlay span::after {
259
+ content: 'stop';
260
+ display: block;
261
+ font-size: 60%;
262
+ line-height: 0;
263
+ color: rgba(255 255 255 / 60%);
264
+ }
265
+
266
+ .drl-loading-overlay.new-tab-overlay span::after {
267
+ content: 'close';
268
+ }
269
+
270
+ @keyframes drl-loading-spinner {
271
+ 0% {
272
+ transform: scale(0);
273
+ opacity: 1;
274
+ }
275
+ 100% {
276
+ transform: scale(1);
277
+ opacity: 0;
278
+ }
279
+ }
280
+ `;
281
+ const tabContent = `
282
+ <!DOCTYPE html>
283
+ <html lang='en'>
284
+ <head>
285
+ <meta charset='UTF-8'>
286
+ <meta name='viewport' content='width=device-width, initial-scale=1.0'>
287
+ <title>Loading DRL...</title>
288
+ <style>
289
+ html, body {
290
+ background-color: #151518;
291
+ height: 100%;
292
+ margin: 0;
293
+ padding: 0;
294
+ font-family: Arial, sans-serif;
295
+ text-align: center;
296
+ }
297
+ ${loaderStyles}
298
+ </style>
299
+ </head>
300
+ <body>
301
+ <div class='drl-loading-overlay new-tab-overlay'>
302
+ <div class='drl-loading-spinner'>
303
+ <div></div>
304
+ Loading DRL
305
+ </div>
306
+ <span onclick='window.close()' tabindex='0'></span>
307
+ </div>
308
+ </body>
309
+ </html>
310
+ `;
311
+
312
+ let elementsInjected = false;
313
+ function injectElements() {
314
+ if (elementsInjected) return;
315
+ const style = document.createElement('style');
316
+ style.innerHTML = `
317
+ ${loaderStyles}
318
+
319
+ .drl-loading-overlay {
320
+ opacity: 0;
321
+ transition: opacity 0.3s ease;
322
+ pointer-events: none;
323
+ }
324
+
325
+ :root[drl-link-loading] .drl-loading-overlay {
326
+ opacity: 1;
327
+ pointer-events: all;
328
+ }
329
+ `;
330
+ document.head.append(style);
331
+
332
+ const overlay = document.createElement('div');
333
+ overlay.classList.add('drl-loading-overlay');
334
+ overlay.innerHTML = `
335
+ <div class='drl-loading-spinner'>
336
+ <div></div>
337
+ Loading DRL
338
+ </div>
339
+ <span tabindex='0'></span>
340
+ `;
341
+ overlay.lastElementChild.addEventListener('click', cancelNavigation);
342
+ document.body.prepend(overlay);
343
+ elementsInjected = true;
344
+ }
345
+
346
+ function cancelNavigation() {
347
+ document.documentElement.removeAttribute('drl-link-loading');
348
+ activeNavigation = null;
349
+ }
350
+
351
+ let activeNavigation;
352
+ let linkFeaturesActive = false;
353
+ function addLinkFeatures() {
354
+ if (!linkFeaturesActive) {
355
+ document.addEventListener('click', async (event: any) => {
356
+ const anchor = event.target.closest('a');
357
+ if (anchor) {
358
+ const href = anchor.href;
359
+ const match = href.match(didUrlRegex);
360
+ if (match) {
361
+ const did = match[1];
362
+ const path = match[2];
363
+ const openAsTab = anchor.target === '_blank';
364
+ event.preventDefault();
365
+ try {
366
+ let tab;
367
+ if (openAsTab) {
368
+ tab = window.open('', '_blank');
369
+ tab.document.write(tabContent);
370
+ } else {
371
+ activeNavigation = path;
372
+ // this is to allow for cached DIDs to instantly load without any flash of loading UI
373
+ setTimeout(
374
+ () =>
375
+ document.documentElement.setAttribute('drl-link-loading', ''),
376
+ 50
377
+ );
378
+ }
379
+ const endpoints = await getDwnEndpoints(did);
380
+ if (!endpoints.length) throw null;
381
+ const url = `${endpoints[0].replace(
382
+ trailingSlashRegex,
383
+ ''
384
+ )}/${did}/${path}`;
385
+ if (openAsTab) {
386
+ if (!tab.closed) tab.location.href = url;
387
+ } else if (activeNavigation === path) {
388
+ window.location.href = url;
389
+ }
390
+ } catch (e) {
391
+ if (activeNavigation === path) {
392
+ cancelNavigation();
393
+ }
394
+ throw new Error(
395
+ `DID endpoint resolution failed for the DRL: ${href}`
396
+ );
397
+ }
398
+ }
399
+ }
400
+ });
401
+
402
+ document.addEventListener('pointercancel', resetContextMenuTarget);
403
+ document.addEventListener('pointerdown', async (event: any) => {
404
+ const target = event.composedPath()[0];
405
+ if (
406
+ (event.pointerType === 'mouse' && event.button === 2) ||
407
+ (event.pointerType === 'touch' && event.isPrimary)
408
+ ) {
409
+ resetContextMenuTarget();
410
+ if (target && target?.src?.match(didUrlRegex)) {
411
+ contextMenuTarget = target;
412
+ target.__src__ = target.src;
413
+ const drl = target.src
414
+ .replace(httpToHttpsRegex, 'https:')
415
+ .replace(trailingSlashRegex, '');
416
+ const responseCache = await caches.open('drl');
417
+ const response = await responseCache.match(drl);
418
+ const url = response.headers.get('dwn-composed-url');
419
+ if (url) target.src = url;
420
+ target.addEventListener('pointerup', resetContextMenuTarget, {
421
+ once: true,
422
+ });
423
+ }
424
+ } else if (target === contextMenuTarget) {
425
+ resetContextMenuTarget();
426
+ }
427
+ });
428
+
429
+ linkFeaturesActive = true;
430
+ }
431
+ }
432
+
433
+ let contextMenuTarget;
434
+ async function resetContextMenuTarget(e?: any) {
435
+ if (e?.type === 'pointerup') {
436
+ await new Promise((r) => requestAnimationFrame(r));
437
+ }
438
+ if (contextMenuTarget) {
439
+ contextMenuTarget.src = contextMenuTarget.__src__;
440
+ delete contextMenuTarget.__src__;
441
+ contextMenuTarget = null;
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Activates various polyfills to enable Web5 features in Web environments.
447
+ *
448
+ * @param {object} [options={}] - Configuration options to control the activation of polyfills.
449
+ * @param {boolean} [options.serviceWorker=true] - Option to avoid installation of the Service Worker. Defaults to true, installing the Service Worker.
450
+ * @param {boolean} [options.injectStyles=true] - Option to skip injection of styles for UI related UX polyfills. Defaults to true, injecting styles.
451
+ * @param {boolean} [options.links=true] - Option to skip activation of DRL link features. Defaults to true, activating link features.
452
+ * @param {function} [options.onCacheCheck] - Callback function to handle cache check events, allowing fine-grained control over what DRL request to cache, and for how long.
453
+ * @param {object} [options.onCacheCheck.event] - The event object passed to the callback.
454
+ * @param {object} [options.onCacheCheck.route] - The route object passed to the callback.
455
+ * @returns {object} [options.onCacheCheck.return] - The return object from the callback.
456
+ * @returns {number} [options.onCacheCheck.return.ttl] - Time-to-live for the cached DRL response, in milliseconds.
457
+ *
458
+ * @returns {void}
459
+ *
460
+ * @example
461
+ * // Activate all polyfills with default options, and cache every DRL for 1 minute
462
+ * activatePolyfills({
463
+ * onCacheCheck(event, route){
464
+ * return {
465
+ * ttl: 60_000
466
+ * }
467
+ * }
468
+ * });
469
+ *
470
+ * @example
471
+ * // Activate polyfills, but without Service Worker activation
472
+ * activatePolyfills({ serviceWorker: false });
473
+ */
474
+ export function activatePolyfills(options: any = {}) {
475
+ if (options.serviceWorker !== false) {
476
+ installWorker(options);
477
+ }
478
+ if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
479
+ if (options.injectStyles !== false) {
480
+ if (document.readyState !== 'loading') injectElements();
481
+ else {
482
+ document.addEventListener('DOMContentLoaded', injectElements, {
483
+ once: true,
484
+ });
485
+ }
486
+ }
487
+ if (options.links !== false) addLinkFeatures();
488
+ }
489
+ }