@@ -299,37 +329,37 @@ function injectElements() {
`;
document.head.append(style);
- let overlay = document.createElement('div');
+ const overlay = document.createElement('div');
overlay.classList.add('drl-loading-overlay');
overlay.innerHTML = `
-
+
-
+
`;
overlay.lastElementChild.addEventListener('click', cancelNavigation);
document.body.prepend(overlay);
elementsInjected = true;
}
-function cancelNavigation(){
+function cancelNavigation() {
document.documentElement.removeAttribute('drl-link-loading');
activeNavigation = null;
}
let activeNavigation;
let linkFeaturesActive = false;
-function addLinkFeatures(){
+function addLinkFeatures() {
if (!linkFeaturesActive) {
document.addEventListener('click', async (event: any) => {
- let anchor = event.target.closest('a');
+ const anchor = event.target.closest('a');
if (anchor) {
- let href = anchor.href;
+ const href = anchor.href;
const match = href.match(didUrlRegex);
if (match) {
- let did = match[1];
- let path = match[2];
+ const did = match[1];
+ const path = match[2];
const openAsTab = anchor.target === '_blank';
event.preventDefault();
try {
@@ -337,27 +367,33 @@ function addLinkFeatures(){
if (openAsTab) {
tab = window.open('', '_blank');
tab.document.write(tabContent);
- }
- else {
+ } else {
activeNavigation = path;
// this is to allow for cached DIDs to instantly load without any flash of loading UI
- setTimeout(() => document.documentElement.setAttribute('drl-link-loading', ''), 50);
+ setTimeout(
+ () =>
+ document.documentElement.setAttribute('drl-link-loading', ''),
+ 50
+ );
}
const endpoints = await getDwnEndpoints(did);
if (!endpoints.length) throw null;
- let url = `${endpoints[0].replace(trailingSlashRegex, '')}/${did}/${path}`;
+ const url = `${endpoints[0].replace(
+ trailingSlashRegex,
+ ''
+ )}/${did}/${path}`;
if (openAsTab) {
if (!tab.closed) tab.location.href = url;
- }
- else if (activeNavigation === path) {
+ } else if (activeNavigation === path) {
window.location.href = url;
}
- }
- catch(e) {
+ } catch (e) {
if (activeNavigation === path) {
cancelNavigation();
}
- throw new Error(`DID endpoint resolution failed for the DRL: ${href}`);
+ throw new Error(
+ `DID endpoint resolution failed for the DRL: ${href}`
+ );
}
}
}
@@ -366,21 +402,26 @@ function addLinkFeatures(){
document.addEventListener('pointercancel', resetContextMenuTarget);
document.addEventListener('pointerdown', async (event: any) => {
const target = event.composedPath()[0];
- if ((event.pointerType === 'mouse' && event.button === 2) ||
- (event.pointerType === 'touch' && event.isPrimary)) {
+ if (
+ (event.pointerType === 'mouse' && event.button === 2) ||
+ (event.pointerType === 'touch' && event.isPrimary)
+ ) {
resetContextMenuTarget();
if (target && target?.src?.match(didUrlRegex)) {
contextMenuTarget = target;
target.__src__ = target.src;
- const drl = target.src.replace(httpToHttpsRegex, 'https:').replace(trailingSlashRegex, '');
+ const drl = target.src
+ .replace(httpToHttpsRegex, 'https:')
+ .replace(trailingSlashRegex, '');
const responseCache = await caches.open('drl');
const response = await responseCache.match(drl);
const url = response.headers.get('dwn-composed-url');
if (url) target.src = url;
- target.addEventListener('pointerup', resetContextMenuTarget, { once: true });
+ target.addEventListener('pointerup', resetContextMenuTarget, {
+ once: true,
+ });
}
- }
- else if (target === contextMenuTarget) {
+ } else if (target === contextMenuTarget) {
resetContextMenuTarget();
}
});
@@ -390,9 +431,9 @@ function addLinkFeatures(){
}
let contextMenuTarget;
-async function resetContextMenuTarget(e?: any){
+async function resetContextMenuTarget(e?: any) {
if (e?.type === 'pointerup') {
- await new Promise(r => requestAnimationFrame(r));
+ await new Promise((r) => requestAnimationFrame(r));
}
if (contextMenuTarget) {
contextMenuTarget.src = contextMenuTarget.__src__;
@@ -429,8 +470,8 @@ async function resetContextMenuTarget(e?: any){
* @example
* // Activate polyfills, but without Service Worker activation
* activatePolyfills({ serviceWorker: false });
-*/
-export function activatePolyfills(options: any = {}){
+ */
+export function activatePolyfills(options: any = {}) {
if (options.serviceWorker !== false) {
installWorker(options);
}
@@ -438,9 +479,11 @@ export function activatePolyfills(options: any = {}){
if (options.injectStyles !== false) {
if (document.readyState !== 'loading') injectElements();
else {
- document.addEventListener('DOMContentLoaded', injectElements, { once: true });
+ document.addEventListener('DOMContentLoaded', injectElements, {
+ once: true,
+ });
}
}
if (options.links !== false) addLinkFeatures();
}
-}
\ No newline at end of file
+}
diff --git a/packages/browser/tests/tsconfig.json b/packages/browser/tests/tsconfig.json
new file mode 100644
index 000000000..7c6d2c8e7
--- /dev/null
+++ b/packages/browser/tests/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "compiled",
+ "declarationDir": "compiled/types",
+ "sourceMap": true,
+ },
+ "include": [
+ "../src",
+ ".",
+ ],
+ "exclude": [
+ "./compiled"
+ ]
+}
\ No newline at end of file
diff --git a/packages/browser/tests/web-features.spec.ts b/packages/browser/tests/web-features.spec.ts
new file mode 100644
index 000000000..c6a220c48
--- /dev/null
+++ b/packages/browser/tests/web-features.spec.ts
@@ -0,0 +1,11 @@
+import { expect } from 'chai';
+
+import { activatePolyfills } from '../src/web-features.js';
+
+describe('web features', () => {
+ describe('activatePolyfills', () => {
+ it('does not throw', () => {
+ expect(() => activatePolyfills()).to.not.throw();
+ });
+ });
+});
diff --git a/packages/browser/tsconfig.json b/packages/browser/tsconfig.json
new file mode 100644
index 000000000..ad92cd4ef
--- /dev/null
+++ b/packages/browser/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "lib": ["DOM", "ES6", "WebWorker"],
+ "strict": false,
+ "declarationDir": "dist/types",
+ "outDir": "dist/esm"
+ },
+ "include": [
+ "src"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/packages/browser/web-test-runner.config.cjs b/packages/browser/web-test-runner.config.cjs
new file mode 100644
index 000000000..e5adb980d
--- /dev/null
+++ b/packages/browser/web-test-runner.config.cjs
@@ -0,0 +1,30 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const playwrightLauncher =
+ require('@web/test-runner-playwright').playwrightLauncher;
+
+/**
+ * @type {import('@web/test-runner').TestRunnerConfig}
+ */
+module.exports = {
+ files : 'tests/compiled/**/*.spec.js',
+ playwright : true,
+ nodeResolve : true,
+ browsers : [
+ playwrightLauncher({
+ product: 'chromium',
+ }),
+ playwrightLauncher({
+ product: 'firefox',
+ }),
+ playwrightLauncher({
+ product: 'webkit',
+ }),
+ ],
+ testsFinishTimeout : 300000,
+ concurrentBrowsers : 2,
+ testFramework : {
+ config: {
+ timeout: '15000',
+ },
+ },
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index acf5b49c2..05e5de743 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -261,6 +261,76 @@ importers:
specifier: 5.1.6
version: 5.1.6
+ packages/browser:
+ dependencies:
+ '@web5/dids':
+ specifier: workspace:*
+ version: link:../dids
+ devDependencies:
+ '@playwright/test':
+ specifier: 1.45.3
+ version: 1.45.3
+ '@types/chai':
+ specifier: 4.3.6
+ version: 4.3.6
+ '@types/eslint':
+ specifier: 8.56.10
+ version: 8.56.10
+ '@types/mocha':
+ specifier: 10.0.1
+ version: 10.0.1
+ '@types/sinon':
+ specifier: 17.0.3
+ version: 17.0.3
+ '@typescript-eslint/eslint-plugin':
+ specifier: 7.9.0
+ version: 7.9.0(@typescript-eslint/parser@7.14.1(eslint@9.3.0)(typescript@5.1.6))(eslint@9.3.0)(typescript@5.1.6)
+ '@typescript-eslint/parser':
+ specifier: 7.14.1
+ version: 7.14.1(eslint@9.3.0)(typescript@5.1.6)
+ '@web/test-runner':
+ specifier: 0.18.2
+ version: 0.18.2
+ '@web/test-runner-playwright':
+ specifier: 0.11.0
+ version: 0.11.0
+ c8:
+ specifier: 9.1.0
+ version: 9.1.0
+ chai:
+ specifier: 4.3.10
+ version: 4.3.10
+ esbuild:
+ specifier: 0.19.8
+ version: 0.19.8
+ eslint:
+ specifier: 9.3.0
+ version: 9.3.0
+ eslint-plugin-mocha:
+ specifier: 10.4.3
+ version: 10.4.3(eslint@9.3.0)
+ mocha:
+ specifier: 10.2.0
+ version: 10.2.0
+ mocha-junit-reporter:
+ specifier: 2.2.1
+ version: 2.2.1(mocha@10.2.0)
+ playwright:
+ specifier: 1.45.3
+ version: 1.45.3
+ rimraf:
+ specifier: 4.4.0
+ version: 4.4.0
+ sinon:
+ specifier: 18.0.0
+ version: 18.0.0
+ source-map-loader:
+ specifier: 4.0.2
+ version: 4.0.2(webpack@5.93.0(esbuild@0.19.8))
+ typescript:
+ specifier: 5.1.6
+ version: 5.1.6
+
packages/common:
dependencies:
'@isaacs/ttlcache':
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index bf5d0d166..50ad8dd32 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -9,3 +9,4 @@ packages:
- "packages/proxy-agent"
- "packages/identity-agent"
- "packages/api"
+ - "packages/browser"
diff --git a/web5-js.code-workspace b/web5-js.code-workspace
index ef7448c45..61d805d72 100644
--- a/web5-js.code-workspace
+++ b/web5-js.code-workspace
@@ -15,6 +15,11 @@
"name": "api",
"path": "packages/api",
},
+ {
+ //@web5/browser
+ "name": "browser",
+ "path": "packages/browser",
+ },
{
// @web5/common
"name": "common",