@aegisjsproject/atlas 1.0.1 → 1.1.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.
- package/.pre-commit-config.yaml +22 -0
- package/CHANGELOG.md +18 -0
- package/README.md +47 -47
- package/atlas.cjs +70 -18
- package/atlas.min.js +1 -1
- package/atlas.min.js.map +1 -1
- package/package.json +11 -8
- package/router.js +64 -19
- package/routes.js +7 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
3
|
+
rev: v8.16.3
|
|
4
|
+
hooks:
|
|
5
|
+
- id: gitleaks
|
|
6
|
+
- repo: local
|
|
7
|
+
hooks:
|
|
8
|
+
- id: local-stylelint
|
|
9
|
+
name: Run Stylelint
|
|
10
|
+
entry: npm run lint:css --if-exists
|
|
11
|
+
language: node
|
|
12
|
+
types: [css]
|
|
13
|
+
- id: local-htmlhint
|
|
14
|
+
name: Run HTMLHint
|
|
15
|
+
entry: npm run lint:html --if-exists
|
|
16
|
+
language: node
|
|
17
|
+
types: [html]
|
|
18
|
+
- id: local-eslint
|
|
19
|
+
name: Run ESLint
|
|
20
|
+
entry: npm run lint:js --if-exists
|
|
21
|
+
language: node
|
|
22
|
+
types_or: [javascript, jsx, ts, tsx]
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [v1.1.0] - 2026-06-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Add `.npmrc` to harden `npm i` & `npm ci` operations
|
|
14
|
+
- Added `SECURITY.md` for reporting security issues
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Update to node 26.3.0 / npm 11.16.0
|
|
18
|
+
- Update from `publish` to `publish stage`
|
|
19
|
+
- Implement security changes provided by SecurityStep
|
|
20
|
+
|
|
21
|
+
## [v1.0.2] - 2026-04-30
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Implement View Transitions (resolves #6)
|
|
25
|
+
- Support router handling on initial load (resolves #5)
|
|
26
|
+
- Handle page metadata via functions (resolves #4)
|
|
27
|
+
|
|
10
28
|
## [v1.0.1] - 22026-04-29
|
|
11
29
|
|
|
12
30
|
### Fixed
|
package/README.md
CHANGED
|
@@ -26,12 +26,17 @@ A client-side router library using `Navigation` & `URLPattern`
|
|
|
26
26
|
|
|
27
27
|
- [Code of Conduct](./.github/CODE_OF_CONDUCT.md)
|
|
28
28
|
- [Contributing](./.github/CONTRIBUTING.md)
|
|
29
|
-
|
|
29
|
+
- [Security Policy](./.github/SECURITY.md)
|
|
30
30
|
|
|
31
31
|
## Overview
|
|
32
32
|
|
|
33
33
|
This router intercepts same-origin navigations and resolves them to registered route modules. Each module can return content in multiple native formats (e.g. `Response`, `Document`, `Element`), allowing flexibility without imposing rendering constraints.
|
|
34
34
|
|
|
35
|
+
> [!IMPORTANT]
|
|
36
|
+
> This requires the [Navigation API](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API), which is Baseline 2026.
|
|
37
|
+
> It also creates a [Trusted Types Policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API), where supported,
|
|
38
|
+
> labeled `"aegis-atlas#html"` for handling HTML responses without sanitizer restrictions.
|
|
39
|
+
|
|
35
40
|
Key characteristics:
|
|
36
41
|
|
|
37
42
|
- Native Navigation API (`navigation`)
|
|
@@ -42,11 +47,6 @@ Key characteristics:
|
|
|
42
47
|
- Optional preload observation
|
|
43
48
|
- Abort-safe lifecycle with `AbortController` and `DisposableStack`
|
|
44
49
|
|
|
45
|
-
> [!IMPORTANT]
|
|
46
|
-
> This requires the [Navigation API](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API), which is Baseline 2026.
|
|
47
|
-
> It also creates a [Trusted Types Policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API), where supported,
|
|
48
|
-
> labeled `"aegis-atlas#html"` for handling HTML responses without sanitizer restrictions.
|
|
49
|
-
|
|
50
50
|
> [!TIP]
|
|
51
51
|
> Route module specifiers can use bare specifiers like `@acme/blog`. These can be resolved via an import map, for example:
|
|
52
52
|
>
|
|
@@ -61,7 +61,7 @@ Key characteristics:
|
|
|
61
61
|
> ```
|
|
62
62
|
>
|
|
63
63
|
> This allows modules to be loaded from a CDN without changing route definitions.
|
|
64
|
-
|
|
64
|
+
- - -
|
|
65
65
|
|
|
66
66
|
## Installation
|
|
67
67
|
|
|
@@ -73,7 +73,7 @@ import { init } from '@aegisjsproject/atlas';
|
|
|
73
73
|
|
|
74
74
|
No dependencies required.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
- - -
|
|
77
77
|
|
|
78
78
|
## Core Concepts
|
|
79
79
|
|
|
@@ -84,9 +84,9 @@ Each route resolves to a module with the following shape:
|
|
|
84
84
|
```js
|
|
85
85
|
|
|
86
86
|
export default async function handler(request, context) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
return new Response('<h1>Hello</h1>', {
|
|
88
|
+
headers: { 'Content-Type': 'text/html' }
|
|
89
|
+
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export const title = 'Page Title';
|
|
@@ -105,7 +105,7 @@ export const styles = new CSSStyleSheet();
|
|
|
105
105
|
- `description` (optional)
|
|
106
106
|
- `styles` (optional: `CSSStyleSheet` or array)
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
- - -
|
|
109
109
|
|
|
110
110
|
### Handler Return Types
|
|
111
111
|
|
|
@@ -115,7 +115,7 @@ Handlers may return:
|
|
|
115
115
|
- `Element`
|
|
116
116
|
- `DocumentFragment`
|
|
117
117
|
- `URL` (triggers navigation)
|
|
118
|
-
|
|
118
|
+
- - -
|
|
119
119
|
|
|
120
120
|
### Route Context
|
|
121
121
|
|
|
@@ -124,21 +124,21 @@ Each handler receives a `context` object:
|
|
|
124
124
|
```js
|
|
125
125
|
{
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
result, // URLPatternResult
|
|
128
|
+
params, // extracted route params
|
|
129
|
+
stack, // DisposableStack
|
|
130
|
+
controller, // AbortController
|
|
131
|
+
signal, // AbortSignal
|
|
132
|
+
type, // navigation type
|
|
133
|
+
url, // URL instance
|
|
134
|
+
state, // navigation state
|
|
135
|
+
info, // navigation info
|
|
136
|
+
timestamp // performance timestamp
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
- - -
|
|
142
142
|
|
|
143
143
|
## Usage
|
|
144
144
|
|
|
@@ -147,13 +147,13 @@ Each handler receives a `context` object:
|
|
|
147
147
|
```js
|
|
148
148
|
|
|
149
149
|
init({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
'/': '/routes/home.js',
|
|
151
|
+
'/users/:id': '/routes/user.js'
|
|
152
|
+
'/posts/:year(\\d{4})/:month(\\d{2})/:day(\\d{2})/:slug': '@acme/blog',
|
|
153
|
+
'/product/:sku': '@acme/store/product',
|
|
154
154
|
}, {
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
root: 'app',
|
|
156
|
+
preload: true
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
```
|
|
@@ -164,7 +164,7 @@ init({
|
|
|
164
164
|
- `preload`: Enable preload observation
|
|
165
165
|
- `signal`: Optional `AbortSignal` for teardown
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
- - -
|
|
168
168
|
|
|
169
169
|
### Navigation Helpers
|
|
170
170
|
|
|
@@ -178,7 +178,7 @@ forward();
|
|
|
178
178
|
reload();
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
- - -
|
|
182
182
|
|
|
183
183
|
### Navigation Lifecycle
|
|
184
184
|
|
|
@@ -190,7 +190,7 @@ import { whenLoaded } from './router.js';
|
|
|
190
190
|
await whenLoaded();
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
- - -
|
|
194
194
|
|
|
195
195
|
## Behavior Details
|
|
196
196
|
|
|
@@ -202,7 +202,7 @@ Navigation is intercepted only if:
|
|
|
202
202
|
- URL is same-origin
|
|
203
203
|
- Triggering element does **not** have `.no-router`
|
|
204
204
|
|
|
205
|
-
|
|
205
|
+
- - -
|
|
206
206
|
|
|
207
207
|
### Content Handling
|
|
208
208
|
|
|
@@ -227,7 +227,7 @@ Navigation is intercepted only if:
|
|
|
227
227
|
|
|
228
228
|
- Triggers navigation
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
- - -
|
|
231
231
|
|
|
232
232
|
### Root Management
|
|
233
233
|
|
|
@@ -245,7 +245,7 @@ If root is `<body>`, full body is replaced.
|
|
|
245
245
|
|
|
246
246
|
If root is an element with `id`, only matching subtree is replaced.
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
- - -
|
|
249
249
|
|
|
250
250
|
### Metadata Updates
|
|
251
251
|
|
|
@@ -258,7 +258,7 @@ Route modules can define:
|
|
|
258
258
|
- `twitter:description`
|
|
259
259
|
- `styles` → appended to `document.adoptedStyleSheets`
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
- - -
|
|
262
262
|
|
|
263
263
|
### Form Handling
|
|
264
264
|
|
|
@@ -266,7 +266,7 @@ Route modules can define:
|
|
|
266
266
|
- Submits `FormData` when applicable
|
|
267
267
|
- Uses `Request` API for consistency
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
- - -
|
|
270
270
|
|
|
271
271
|
### Abort + Cleanup
|
|
272
272
|
|
|
@@ -278,7 +278,7 @@ Each navigation:
|
|
|
278
278
|
|
|
279
279
|
Handlers should respect `context.signal` where applicable.
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
- - -
|
|
282
282
|
|
|
283
283
|
### Trusted Types
|
|
284
284
|
|
|
@@ -296,18 +296,18 @@ This ensures CSP compatibility without stripping critical markup like:
|
|
|
296
296
|
- inline event handlers
|
|
297
297
|
- form attributes
|
|
298
298
|
|
|
299
|
-
|
|
299
|
+
- - -
|
|
300
300
|
|
|
301
301
|
## Example Route
|
|
302
302
|
|
|
303
303
|
```js
|
|
304
304
|
|
|
305
305
|
export default async function(request, { params }) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
306
|
+
return new Response(`
|
|
307
|
+
<h1>User ${params.id}</h1>
|
|
308
|
+
`, {
|
|
309
|
+
headers: { 'Content-Type': 'text/html' }
|
|
310
|
+
});
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
export const title = 'User Profile';
|
|
@@ -315,7 +315,7 @@ export const description = 'User details page';
|
|
|
315
315
|
|
|
316
316
|
```
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
- - -
|
|
319
319
|
|
|
320
320
|
## Notes
|
|
321
321
|
|
|
@@ -324,7 +324,7 @@ export const description = 'User details page';
|
|
|
324
324
|
- Errors during routing are surfaced via `reportError`
|
|
325
325
|
- Designed for modern browsers with Navigation API support
|
|
326
326
|
|
|
327
|
-
|
|
327
|
+
- - -
|
|
328
328
|
|
|
329
329
|
## Summary
|
|
330
330
|
|
package/atlas.cjs
CHANGED
|
@@ -32,6 +32,13 @@ const invalidMatchResult = Object.freeze({ result: null, specifier: null, hasReg
|
|
|
32
32
|
*/
|
|
33
33
|
const getRegistryKey = url => reg.keys().find(pattern => pattern.test(url));
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a route for the given URL is registered
|
|
37
|
+
* @param {string} [url=location.href]
|
|
38
|
+
* @return {boolean}
|
|
39
|
+
*/
|
|
40
|
+
const hasRegistryKey = (url = location.href) => reg.keys().some(pattern => pattern.test(url));
|
|
41
|
+
|
|
35
42
|
/**
|
|
36
43
|
*
|
|
37
44
|
* @param {URLPattern} key
|
|
@@ -365,7 +372,8 @@ const DESC_SELECTOR = 'meta[name="description"], meta[itemprop="description"], m
|
|
|
365
372
|
* @property {AbortController} controller
|
|
366
373
|
* @property {AbortSignal} signal
|
|
367
374
|
* @property {NavigationType} type
|
|
368
|
-
* @property {URL}
|
|
375
|
+
* @property {URL} newURL
|
|
376
|
+
* @property {URL} oldURL
|
|
369
377
|
* @property {any} state
|
|
370
378
|
* @property {any} info
|
|
371
379
|
* @property {number} timestamp
|
|
@@ -422,8 +430,9 @@ function getRequestMethod(source) {
|
|
|
422
430
|
/**
|
|
423
431
|
*
|
|
424
432
|
* @param {NavigationEvent} event
|
|
433
|
+
* @param {URL} oldURL
|
|
425
434
|
*/
|
|
426
|
-
async function handleNavigation(event) {
|
|
435
|
+
async function handleNavigation(event, oldURL = new URL(location.href)) {
|
|
427
436
|
if (! (event instanceof NavigateEvent)) {
|
|
428
437
|
throw new TypeError('Not a navigation event.');
|
|
429
438
|
} else if (event.signal.aborted) {
|
|
@@ -433,15 +442,18 @@ async function handleNavigation(event) {
|
|
|
433
442
|
const request = new Request(event.destination.url, {
|
|
434
443
|
// `sourceElement` could be a form, a `<button type="submit">`, or an `<a>
|
|
435
444
|
method: method,
|
|
436
|
-
body: method === 'GET' ? undefined : event.formData
|
|
445
|
+
body: method === 'GET' ? undefined : event.formData,
|
|
437
446
|
signal: event.signal,
|
|
447
|
+
mode: 'same-origin',
|
|
448
|
+
credentials: 'include',
|
|
449
|
+
priority: 'high',
|
|
438
450
|
});
|
|
439
451
|
|
|
440
452
|
const { result, specifier, hasRegExpGroups } = lookupRoute(event.destination.url);
|
|
441
453
|
|
|
442
454
|
if (typeof specifier !== 'string' || result === null) {
|
|
443
455
|
const resp = await fetch(request);
|
|
444
|
-
await updateContent(resp);
|
|
456
|
+
await updateContent(resp, {});
|
|
445
457
|
} else {
|
|
446
458
|
const params = hasRegExpGroups ? {
|
|
447
459
|
...result.protocol.groups, ...result.username.groups, ...result.password.groups, ...result.hostname.groups,
|
|
@@ -459,6 +471,12 @@ async function handleNavigation(event) {
|
|
|
459
471
|
const timestamp = performance.now();
|
|
460
472
|
const signal = AbortSignal.any([controller.signal, request.signal]);
|
|
461
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Dispose of stack on next navigation or on error, triggering clean-up
|
|
476
|
+
*/
|
|
477
|
+
navigation.addEventListener('navigate', () => stack.dispose(), { once: true, signal });
|
|
478
|
+
navigation.addEventListener('navigateerror', () => stack.dispose(), { once: true, signal });
|
|
479
|
+
|
|
462
480
|
/**
|
|
463
481
|
* @type {RouteContextObject}
|
|
464
482
|
*/
|
|
@@ -469,7 +487,8 @@ async function handleNavigation(event) {
|
|
|
469
487
|
type: event.navigationType,
|
|
470
488
|
state: event.destination.getState(),
|
|
471
489
|
info: event.info,
|
|
472
|
-
|
|
490
|
+
newURL: new URL(event.destination.url),
|
|
491
|
+
oldURL,
|
|
473
492
|
signal,
|
|
474
493
|
result,
|
|
475
494
|
params,
|
|
@@ -479,8 +498,7 @@ async function handleNavigation(event) {
|
|
|
479
498
|
return await handleRequestModule(request, context, module);
|
|
480
499
|
} catch(err) {
|
|
481
500
|
reportError(err);
|
|
482
|
-
|
|
483
|
-
stack.dispose();
|
|
501
|
+
stack.dispose(); // Ensure clean-up happens even in an error
|
|
484
502
|
}
|
|
485
503
|
}
|
|
486
504
|
}
|
|
@@ -513,14 +531,19 @@ function init(routes, {
|
|
|
513
531
|
}
|
|
514
532
|
|
|
515
533
|
navigation.addEventListener('navigate', event => {
|
|
534
|
+
const oldURL = new URL(location.href);
|
|
516
535
|
if (event.canIntercept && event.destination.url.startsWith(location.origin) && ! event.sourceElement?.classList?.contains?.('no-router')) {
|
|
517
|
-
event.intercept({ handler: () => handleNavigation(event) });
|
|
536
|
+
event.intercept({ handler: () => handleNavigation(event, oldURL) });
|
|
518
537
|
}
|
|
519
538
|
}, { signal });
|
|
520
539
|
|
|
521
540
|
if (preload) {
|
|
522
541
|
observePreloadsOn(document.body);
|
|
523
542
|
}
|
|
543
|
+
|
|
544
|
+
if (hasRegistryKey(location.href)) {
|
|
545
|
+
navigation.reload({ info: 'Initial Load', state: navigation.currentEntry.getState() });
|
|
546
|
+
}
|
|
524
547
|
} else {
|
|
525
548
|
throw new TypeError(`Routes must be an object, \`<script>\`, or name/index of \`document.scripts\`. Got a ${typeof routes}.`);
|
|
526
549
|
}
|
|
@@ -604,28 +627,42 @@ async function handleRequestModule(request, context, module) {
|
|
|
604
627
|
if (typeof module.default === 'undefined') {
|
|
605
628
|
throw new TypeError(`No default export in module for <${request.url}>.`);
|
|
606
629
|
} else if (typeof module.default === 'function') {
|
|
607
|
-
const result =
|
|
608
|
-
|
|
609
|
-
|
|
630
|
+
const result = module.default.prototype instanceof HTMLElement
|
|
631
|
+
? new module.default(request, context)
|
|
632
|
+
: await module.default(request, context);
|
|
633
|
+
|
|
634
|
+
await updateContent(result, module);
|
|
635
|
+
updateMeta(module, context);
|
|
610
636
|
} else {
|
|
611
637
|
await updateContent(module.default);
|
|
612
|
-
updateMeta(module);
|
|
638
|
+
updateMeta(module, context);
|
|
613
639
|
}
|
|
614
640
|
}
|
|
615
641
|
|
|
616
|
-
function updateMeta({ title, description, styles }) {
|
|
642
|
+
async function updateMeta({ title, description, styles }, context) {
|
|
617
643
|
if (typeof title === 'string') {
|
|
618
644
|
document.title = title;
|
|
645
|
+
} else if (typeof title === 'function') {
|
|
646
|
+
document.title = await title(context);
|
|
619
647
|
}
|
|
620
648
|
|
|
621
649
|
if (typeof description === 'string') {
|
|
622
650
|
setDescription(description);
|
|
651
|
+
} else if (typeof description === 'function') {
|
|
652
|
+
setDescription(await description(context));
|
|
623
653
|
}
|
|
624
654
|
|
|
625
655
|
if (styles instanceof CSSStyleSheet) {
|
|
626
656
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
|
|
627
657
|
} else if (Array.isArray(styles) && styles.length !== 0) {
|
|
628
658
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styles];
|
|
659
|
+
} else if (typeof styles === 'function') {
|
|
660
|
+
const newStyles = await styles(context);
|
|
661
|
+
if (newStyles instanceof CSSStyleSheet) {
|
|
662
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, newStyles];
|
|
663
|
+
} else if (Array.isArray(newStyles) && newStyles.length !== 0) {
|
|
664
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...newStyles];
|
|
665
|
+
}
|
|
629
666
|
}
|
|
630
667
|
}
|
|
631
668
|
|
|
@@ -633,7 +670,7 @@ function updateMeta({ title, description, styles }) {
|
|
|
633
670
|
*
|
|
634
671
|
* @param {HandlerResult} content
|
|
635
672
|
*/
|
|
636
|
-
async function updateContent(content) {
|
|
673
|
+
async function updateContent(content, { viewTransitionTypes: types = [] } = {}) {
|
|
637
674
|
if (content instanceof URL) {
|
|
638
675
|
navigate(content);
|
|
639
676
|
} else if (content instanceof Response) {
|
|
@@ -645,18 +682,33 @@ async function updateContent(content) {
|
|
|
645
682
|
const html = await content.text();
|
|
646
683
|
/** @type HTMLDocument */
|
|
647
684
|
const doc = Document.parseHTMLUnsafe(policy.createHTML(html)); // Unsafe, but necessary... Same-origin at least
|
|
648
|
-
await updateContent(doc);
|
|
685
|
+
await updateContent(doc, { viewTransitionTypes: types });
|
|
649
686
|
}
|
|
650
687
|
} else if (content instanceof Element || content instanceof DocumentFragment) {
|
|
651
|
-
|
|
688
|
+
await document.startViewTransition({
|
|
689
|
+
types,
|
|
690
|
+
update() {
|
|
691
|
+
root.replaceChildren(content);
|
|
692
|
+
},
|
|
693
|
+
});
|
|
652
694
|
} else if (content instanceof HTMLDocument) {
|
|
653
695
|
document.title = content.title;
|
|
654
696
|
setDescription(content.head.querySelector(DESC_SELECTOR)?.content);
|
|
655
697
|
|
|
656
698
|
if (root instanceof HTMLBodyElement) {
|
|
657
|
-
|
|
699
|
+
await document.startViewTransition({
|
|
700
|
+
types,
|
|
701
|
+
update() {
|
|
702
|
+
root.replaceChildren(...content.body.childNodes);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
658
705
|
} else if (root instanceof HTMLElement && typeof root.id === 'string') {
|
|
659
|
-
|
|
706
|
+
await document.startViewTransition({
|
|
707
|
+
types,
|
|
708
|
+
update() {
|
|
709
|
+
root.replaceChildren(...content.getElementById(root.id)?.childNodes ?? []);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
660
712
|
} else {
|
|
661
713
|
throw new TypeError('Root must be `<body>` or an element with an `id`.');
|
|
662
714
|
}
|
package/atlas.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=new Map,t=new Map,n=/^(?:\.*\/)+/,r=Object.freeze({result:null,specifier:null,hasRegExpGroups:!1}),o=t=>e.keys().find((e=>e.test(t))),i=t=>e.get(t);function a(n){if(t.has(n))return t.get(n);{const i=o(n);if(i instanceof URLPattern){const r=Object.freeze({result:i.exec(n),specifier:e.get(i),hasRegExpGroups:i.hasRegExpGroups});return t.set(n,r),r}return t.set(n,r),r}}function s(e){return(e=>!n.test(e))(e)?import.meta.resolve(e):URL.canParse(e)?e:e instanceof URL?e.href:new URL(e,document.baseURI).href}function c(t,n){if(!("string"==typeof n||n instanceof URL))throw new TypeError(`Invalid specifier type ${typeof n}.`);if("string"==typeof t)e.set(URL.canParse(t)?new URLPattern(t):new URLPattern({pathname:t}),s(n));else{if(!(t instanceof URLPattern))throw new TypeError(`Invalid pattner "${t}".`);e.set(t,s(n))}}function l(e,{relList:t=[],crossOrigin:n="anonymous",referrerPolicy:r="no-referrer",fetchPriority:o="auto",signal:i,as:a,integrity:s,media:c,type:l}={}){const{promise:f,resolve:d,reject:p}=Promise.withResolvers(),g=document.createElement("link");if(i instanceof AbortSignal&&i.aborted)p(i.reason);else{if("string"==typeof e||e instanceof URL){if(g.relList.add(...t),"string"==typeof o&&(g.fetchPriority=o),"string"==typeof n&&(g.crossOrigin=n),"string"==typeof l&&(g.type=l),"string"==typeof c?g.media=c:c instanceof MediaQueryList&&(g.media=c.media),"string"==typeof a&&(g.as=a),"string"==typeof s&&(g.integrity=s),g.relList.contains("preload")||g.relList.contains("modulepreload")){const t=new AbortController,n=i instanceof AbortSignal?AbortSignal.any([t.signal,i]):t.signal;return i instanceof AbortSignal&&i.addEventListener("abort",(({target:e})=>{p(e.reason)}),{signal:t.signal,once:!0}),g.referrerPolicy=r,g.addEventListener("load",(()=>{d(),t.abort()}),{signal:n}),g.addEventListener("error",(()=>{p(new DOMException(`Error loading ${e}`,"NotFoundError")),t.abort()}),{signal:n}),g.href=e,document.head.append(g),f.catch(reportError).finally((()=>g.isConnected&&g.remove()))}return g.href=e,document.head.append(g),d(),f}p(new TypeError(`Invalid href to preload: "${e}.`))}}function f(e){e instanceof MutationRecord?f(e.target):"A"!==e.tagName||e.classList.contains("no-router")?e.querySelectorAll("a:not(.no-router)").forEach((e=>u(e,e.dataset))):u(e,e.dataset)}const d=new MutationObserver((e=>e.forEach(f)));async function p(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="low",as:o="script",signal:i,integrity:a}={}){await l(e,{relList:["modulepreload"],crossOrigin:t,referrerPolicy:n,fetchPriority:r,as:o,signal:i,integrity:a})}async function g(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="auto",signal:o,as:i,integrity:a,media:s,type:c}={}){await l(e,{relList:["preload"],crossOrigin:t,referrerPolicy:n,fetchPriority:r,as:i,signal:o,type:c,media:s,integrity:a})}async function u(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="high",signal:a}={}){const{resolve:s,reject:c,promise:l}=Promise.withResolvers();"string"==typeof e?await Promise.all(Array.from(document.querySelectorAll(e),(e=>u(e)))).then(s,c):e instanceof HTMLElement&&!e.classList.contains("no-router")&&"string"==typeof e.href&&e.origin===location.origin&&0===e.download.length&&URL.canParse(e.href)?e.addEventListener("mouseover",(async({currentTarget:e})=>{const c=o(e.href);c instanceof URLPattern?(await p(i(c),{fetchPriority:r,referrerPolicy:n,crossOrigin:t,integrity:e.dataset.integrity,signal:a}),s()):(await g(e.href,{fetchPriority:r,crossOrigin:t,referrerPolicy:n,as:e.dataset.preloadAs??"fetch",type:e.dataset.preloadType??"text/html",integrity:e.dataset.integrity,signal:a}),s())}),{once:!0,passive:!0,signal:a}):s(),await l}function y(e,t=document.documentElement){if("string"==typeof e)y(t.querySelector(e));else{if(!(e instanceof HTMLElement||e instanceof ShadowRoot))throw new TypeError("`observePreloadsOn` requires a selector or HTMLElement or ShadowRoot.");d.observe(e,{childList:!0,subtree:!0}),f(e)}}const m="trustedTypes"in globalThis?trustedTypes.createPolicy("aegis-atlas#html",{createHTML:e=>e}):Object.freeze({createHTML:e=>e}),h='meta[name="description"], meta[itemprop="description"], meta[property="og:description"], meta[name="twitter:description"]';let w=document.body;function E(e,t=document){if("string"==typeof e)E(t.getElementById(e));else{if(!(e instanceof HTMLElement))throw new TypeError("New root must be an `Element` or `id` of an element.");w=e}}async function L(e){if(!(e instanceof NavigateEvent))throw new TypeError("Not a navigation event.");if(e.signal.aborted)throw e.signal.reason;{const n=!((t=e.sourceElement)instanceof HTMLElement)||t instanceof HTMLAnchorElement?"GET":t instanceof HTMLFormElement?t.method.toUpperCase():t instanceof HTMLButtonElement?t.hasAttribute("formmethod")&&0!==t.formMethod.length?t.formMethod.toUpperCase():t.form instanceof HTMLFormElement?t.form.method.toUpperCase():(console.warn("Not sure this should be possible..."),"GET"):"GET",r=new Request(e.destination.url,{method:n,body:"GET"===n?void 0:e.formData,signal:e.signal}),{result:o,specifier:i,hasRegExpGroups:s}=a(e.destination.url);if("string"!=typeof i||null===o){const e=await fetch(r);await A(e)}else{const t=s?{...o.protocol.groups,...o.username.groups,...o.password.groups,...o.hostname.groups,...o.port.groups,...o.pathname.groups,...o.search.groups,...o.hash.groups}:{};delete t[0];const n=await import(i),a=new DisposableStack,c=a.adopt(new AbortController,(e=>e.abort(new DOMException("Stack was disposed.","AbortError")))),l=performance.now(),f=AbortSignal.any([c.signal,r.signal]),d=Object.freeze({timestamp:l,stack:a,controller:c,type:e.navigationType,state:e.destination.getState(),info:e.info,url:new URL(e.destination.url),signal:f,result:o,params:t});try{return await async function(e,t,n){if(void 0===n.default)throw new TypeError(`No default export in module for <${e.url}>.`);if("function"==typeof n.default){const r=await n.default(e,t);await A(r),R(n)}else await A(n.default),R(n)}(r,d,n)}catch(e){reportError(e)}finally{a.dispose()}}}var t}function b(e,{root:t,preload:n=!1,signal:r}={}){if("string"==typeof e)b(JSON.parse(document.scripts.namedItem(e).innerHTML),{root:t,preload:n,signal:r});else if("number"==typeof e)b(JSON.parse(document.scripts.item(e).innerHTML),{root:t,preload:n,signal:r});else if(e instanceof HTMLScriptElement)b(JSON.parse(e.textContent),{root:t,preload:n,signal:r});else{if("object"!=typeof e)throw new TypeError(`Routes must be an object, \`<script>\`, or name/index of \`document.scripts\`. Got a ${typeof e}.`);Object.entries(e).forEach((([e,t])=>c(e,t))),("string"==typeof t||t instanceof HTMLElement)&&E(t),navigation.addEventListener("navigate",(e=>{e.canIntercept&&e.destination.url.startsWith(location.origin)&&!e.sourceElement?.classList?.contains?.("no-router")&&e.intercept({handler:()=>L(e)})}),{signal:r}),n&&y(document.body)}}async function T({signal:e}={}){const{resolve:t,reject:n,promise:r}=Promise.withResolvers();if(e?.aborted)n(e.reason);else{const r=new AbortController,o={once:!0,signal:e instanceof AbortSignal?AbortSignal.any([e,r.signal]):r.signal};navigation.addEventListener("navigatesuccess",(()=>{t(navigation.currentEntry),r.abort()}),o),navigation.addEventListener("navigateerror",(e=>{n(e.error),r.abort()}),o),e instanceof AbortSignal&&e.addEventListener("abort",(({target:e})=>{n(e.reason),r.abort(e.reason)}),{once:!0,signal:r.signal})}return r}const v=(e,t)=>navigation.navigate(e,t),S=e=>navigation.back(e),P=e=>navigation.forward(e),M=e=>navigation.reload(e);function R({title:e,description:t,styles:n}){"string"==typeof e&&(document.title=e),"string"==typeof t&&O(t),n instanceof CSSStyleSheet?document.adoptedStyleSheets=[...document.adoptedStyleSheets,n]:Array.isArray(n)&&0!==n.length&&(document.adoptedStyleSheets=[...document.adoptedStyleSheets,...n])}async function A(e){if(e instanceof URL)v(e);else if(e instanceof Response){if(!e.ok)throw new DOMException(`${e.url} [${e.status}]`,"NetworkError");if(!e.headers.get("Content-Type")?.startsWith?.("text/html"))throw new TypeError(`Unsupported Content-Type for <${e.url}> - "${e.headers.get("Content-Type")??"Unset"}".`);{const t=await e.text(),n=Document.parseHTMLUnsafe(m.createHTML(t));await A(n)}}else if(e instanceof Element||e instanceof DocumentFragment)w.replaceChildren(e);else{if(!(e instanceof HTMLDocument))throw new TypeError("Content must be an `Element`, `DocumentFragment`, `HTMLDocument`, or `Response`.");if(document.title=e.title,O(e.head.querySelector(h)?.content),w instanceof HTMLBodyElement)w.replaceChildren(...e.body.childNodes);else{if(!(w instanceof HTMLElement&&"string"==typeof w.id))throw new TypeError("Root must be `<body>` or an element with an `id`.");w.replaceChildren(...e.getElementById(w.id)?.childNodes??[])}}}function O(e=""){document.head.querySelectorAll(h).forEach((t=>t.content=e))}export{S as back,P as forward,o as getRegistryKey,i as getRegistrySpecifier,L as handleNavigation,b as init,a as lookupRoute,v as navigate,y as observePreloadsOn,g as preload,p as preloadModule,u as preloadOnHover,c as registerModule,M as reload,E as setRoot,T as whenLoaded};
|
|
1
|
+
const e=new Map,t=new Map,n=/^(?:\.*\/)+/,r=Object.freeze({result:null,specifier:null,hasRegExpGroups:!1}),o=t=>e.keys().find((e=>e.test(t))),i=t=>e.get(t);function a(n){if(t.has(n))return t.get(n);{const i=o(n);if(i instanceof URLPattern){const r=Object.freeze({result:i.exec(n),specifier:e.get(i),hasRegExpGroups:i.hasRegExpGroups});return t.set(n,r),r}return t.set(n,r),r}}function s(e){return(e=>!n.test(e))(e)?import.meta.resolve(e):URL.canParse(e)?e:e instanceof URL?e.href:new URL(e,document.baseURI).href}function c(t,n){if(!("string"==typeof n||n instanceof URL))throw new TypeError(`Invalid specifier type ${typeof n}.`);if("string"==typeof t)e.set(URL.canParse(t)?new URLPattern(t):new URLPattern({pathname:t}),s(n));else{if(!(t instanceof URLPattern))throw new TypeError(`Invalid pattner "${t}".`);e.set(t,s(n))}}function l(e,{relList:t=[],crossOrigin:n="anonymous",referrerPolicy:r="no-referrer",fetchPriority:o="auto",signal:i,as:a,integrity:s,media:c,type:l}={}){const{promise:f,resolve:d,reject:p}=Promise.withResolvers(),u=document.createElement("link");if(i instanceof AbortSignal&&i.aborted)p(i.reason);else{if("string"==typeof e||e instanceof URL){if(u.relList.add(...t),"string"==typeof o&&(u.fetchPriority=o),"string"==typeof n&&(u.crossOrigin=n),"string"==typeof l&&(u.type=l),"string"==typeof c?u.media=c:c instanceof MediaQueryList&&(u.media=c.media),"string"==typeof a&&(u.as=a),"string"==typeof s&&(u.integrity=s),u.relList.contains("preload")||u.relList.contains("modulepreload")){const t=new AbortController,n=i instanceof AbortSignal?AbortSignal.any([t.signal,i]):t.signal;return i instanceof AbortSignal&&i.addEventListener("abort",(({target:e})=>{p(e.reason)}),{signal:t.signal,once:!0}),u.referrerPolicy=r,u.addEventListener("load",(()=>{d(),t.abort()}),{signal:n}),u.addEventListener("error",(()=>{p(new DOMException(`Error loading ${e}`,"NotFoundError")),t.abort()}),{signal:n}),u.href=e,document.head.append(u),f.catch(reportError).finally((()=>u.isConnected&&u.remove()))}return u.href=e,document.head.append(u),d(),f}p(new TypeError(`Invalid href to preload: "${e}.`))}}function f(e){e instanceof MutationRecord?f(e.target):"A"!==e.tagName||e.classList.contains("no-router")?e.querySelectorAll("a:not(.no-router)").forEach((e=>g(e,e.dataset))):g(e,e.dataset)}const d=new MutationObserver((e=>e.forEach(f)));async function p(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="low",as:o="script",signal:i,integrity:a}={}){await l(e,{relList:["modulepreload"],crossOrigin:t,referrerPolicy:n,fetchPriority:r,as:o,signal:i,integrity:a})}async function u(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="auto",signal:o,as:i,integrity:a,media:s,type:c}={}){await l(e,{relList:["preload"],crossOrigin:t,referrerPolicy:n,fetchPriority:r,as:i,signal:o,type:c,media:s,integrity:a})}async function g(e,{crossOrigin:t="anonymous",referrerPolicy:n="no-referrer",fetchPriority:r="high",signal:a}={}){const{resolve:s,reject:c,promise:l}=Promise.withResolvers();"string"==typeof e?await Promise.all(Array.from(document.querySelectorAll(e),(e=>g(e)))).then(s,c):e instanceof HTMLElement&&!e.classList.contains("no-router")&&"string"==typeof e.href&&e.origin===location.origin&&0===e.download.length&&URL.canParse(e.href)?e.addEventListener("mouseover",(async({currentTarget:e})=>{const c=o(e.href);c instanceof URLPattern?(await p(i(c),{fetchPriority:r,referrerPolicy:n,crossOrigin:t,integrity:e.dataset.integrity,signal:a}),s()):(await u(e.href,{fetchPriority:r,crossOrigin:t,referrerPolicy:n,as:e.dataset.preloadAs??"fetch",type:e.dataset.preloadType??"text/html",integrity:e.dataset.integrity,signal:a}),s())}),{once:!0,passive:!0,signal:a}):s(),await l}function y(e,t=document.documentElement){if("string"==typeof e)y(t.querySelector(e));else{if(!(e instanceof HTMLElement||e instanceof ShadowRoot))throw new TypeError("`observePreloadsOn` requires a selector or HTMLElement or ShadowRoot.");d.observe(e,{childList:!0,subtree:!0}),f(e)}}const m="trustedTypes"in globalThis?trustedTypes.createPolicy("aegis-atlas#html",{createHTML:e=>e}):Object.freeze({createHTML:e=>e}),h='meta[name="description"], meta[itemprop="description"], meta[property="og:description"], meta[name="twitter:description"]';let w=document.body;function E(e,t=document){if("string"==typeof e)E(t.getElementById(e));else{if(!(e instanceof HTMLElement))throw new TypeError("New root must be an `Element` or `id` of an element.");w=e}}async function L(e,t=new URL(location.href)){if(!(e instanceof NavigateEvent))throw new TypeError("Not a navigation event.");if(e.signal.aborted)throw e.signal.reason;{const r=!((n=e.sourceElement)instanceof HTMLElement)||n instanceof HTMLAnchorElement?"GET":n instanceof HTMLFormElement?n.method.toUpperCase():n instanceof HTMLButtonElement?n.hasAttribute("formmethod")&&0!==n.formMethod.length?n.formMethod.toUpperCase():n.form instanceof HTMLFormElement?n.form.method.toUpperCase():(console.warn("Not sure this should be possible..."),"GET"):"GET",o=new Request(e.destination.url,{method:r,body:"GET"===r?void 0:e.formData,signal:e.signal,mode:"same-origin",credentials:"include",priority:"high"}),{result:i,specifier:s,hasRegExpGroups:c}=a(e.destination.url);if("string"!=typeof s||null===i){const e=await fetch(o);await U(e,{})}else{const n=c?{...i.protocol.groups,...i.username.groups,...i.password.groups,...i.hostname.groups,...i.port.groups,...i.pathname.groups,...i.search.groups,...i.hash.groups}:{};delete n[0];const r=await import(s),a=new DisposableStack,l=a.adopt(new AbortController,(e=>e.abort(new DOMException("Stack was disposed.","AbortError")))),f=performance.now(),d=AbortSignal.any([l.signal,o.signal]);navigation.addEventListener("navigate",(()=>a.dispose()),{once:!0,signal:d}),navigation.addEventListener("navigateerror",(()=>a.dispose()),{once:!0,signal:d});const p=Object.freeze({timestamp:f,stack:a,controller:l,type:e.navigationType,state:e.destination.getState(),info:e.info,newURL:new URL(e.destination.url),oldURL:t,signal:d,result:i,params:n});try{return await async function(e,t,n){if(void 0===n.default)throw new TypeError(`No default export in module for <${e.url}>.`);if("function"==typeof n.default){const r=n.default.prototype instanceof HTMLElement?new n.default(e,t):await n.default(e,t);await U(r,n),P(n,t)}else await U(n.default),P(n,t)}(o,p,r)}catch(e){reportError(e),a.dispose()}}}var n}function v(t,{root:n,preload:r=!1,signal:o}={}){if("string"==typeof t)v(JSON.parse(document.scripts.namedItem(t).innerHTML),{root:n,preload:r,signal:o});else if("number"==typeof t)v(JSON.parse(document.scripts.item(t).innerHTML),{root:n,preload:r,signal:o});else if(t instanceof HTMLScriptElement)v(JSON.parse(t.textContent),{root:n,preload:r,signal:o});else{if("object"!=typeof t)throw new TypeError(`Routes must be an object, \`<script>\`, or name/index of \`document.scripts\`. Got a ${typeof t}.`);Object.entries(t).forEach((([e,t])=>c(e,t))),("string"==typeof n||n instanceof HTMLElement)&&E(n),navigation.addEventListener("navigate",(e=>{const t=new URL(location.href);e.canIntercept&&e.destination.url.startsWith(location.origin)&&!e.sourceElement?.classList?.contains?.("no-router")&&e.intercept({handler:()=>L(e,t)})}),{signal:o}),r&&y(document.body),((t=location.href)=>e.keys().some((e=>e.test(t))))(location.href)&&navigation.reload({info:"Initial Load",state:navigation.currentEntry.getState()})}}async function T({signal:e}={}){const{resolve:t,reject:n,promise:r}=Promise.withResolvers();if(e?.aborted)n(e.reason);else{const r=new AbortController,o={once:!0,signal:e instanceof AbortSignal?AbortSignal.any([e,r.signal]):r.signal};navigation.addEventListener("navigatesuccess",(()=>{t(navigation.currentEntry),r.abort()}),o),navigation.addEventListener("navigateerror",(e=>{n(e.error),r.abort()}),o),e instanceof AbortSignal&&e.addEventListener("abort",(({target:e})=>{n(e.reason),r.abort(e.reason)}),{once:!0,signal:r.signal})}return r}const b=(e,t)=>navigation.navigate(e,t),S=e=>navigation.back(e),R=e=>navigation.forward(e),M=e=>navigation.reload(e);async function P({title:e,description:t,styles:n},r){if("string"==typeof e?document.title=e:"function"==typeof e&&(document.title=await e(r)),"string"==typeof t?A(t):"function"==typeof t&&A(await t(r)),n instanceof CSSStyleSheet)document.adoptedStyleSheets=[...document.adoptedStyleSheets,n];else if(Array.isArray(n)&&0!==n.length)document.adoptedStyleSheets=[...document.adoptedStyleSheets,...n];else if("function"==typeof n){const e=await n(r);e instanceof CSSStyleSheet?document.adoptedStyleSheets=[...document.adoptedStyleSheets,e]:Array.isArray(e)&&0!==e.length&&(document.adoptedStyleSheets=[...document.adoptedStyleSheets,...e])}}async function U(e,{viewTransitionTypes:t=[]}={}){if(e instanceof URL)b(e);else if(e instanceof Response){if(!e.ok)throw new DOMException(`${e.url} [${e.status}]`,"NetworkError");if(!e.headers.get("Content-Type")?.startsWith?.("text/html"))throw new TypeError(`Unsupported Content-Type for <${e.url}> - "${e.headers.get("Content-Type")??"Unset"}".`);{const n=await e.text(),r=Document.parseHTMLUnsafe(m.createHTML(n));await U(r,{viewTransitionTypes:t})}}else if(e instanceof Element||e instanceof DocumentFragment)await document.startViewTransition({types:t,update(){w.replaceChildren(e)}});else{if(!(e instanceof HTMLDocument))throw new TypeError("Content must be an `Element`, `DocumentFragment`, `HTMLDocument`, or `Response`.");if(document.title=e.title,A(e.head.querySelector(h)?.content),w instanceof HTMLBodyElement)await document.startViewTransition({types:t,update(){w.replaceChildren(...e.body.childNodes)}});else{if(!(w instanceof HTMLElement&&"string"==typeof w.id))throw new TypeError("Root must be `<body>` or an element with an `id`.");await document.startViewTransition({types:t,update(){w.replaceChildren(...e.getElementById(w.id)?.childNodes??[])}})}}}function A(e=""){document.head.querySelectorAll(h).forEach((t=>t.content=e))}export{S as back,R as forward,o as getRegistryKey,i as getRegistrySpecifier,L as handleNavigation,v as init,a as lookupRoute,b as navigate,y as observePreloadsOn,u as preload,p as preloadModule,g as preloadOnHover,c as registerModule,M as reload,E as setRoot,T as whenLoaded};
|
|
2
2
|
//# sourceMappingURL=atlas.min.js.map
|
package/atlas.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atlas.min.js","sources":["routes.js","preload.js","router.js"],"sourcesContent":["/**\n * @type {Map<URLPattern, string>}\n */\nconst reg = new Map();\n\nconst cache = new Map();\n\nconst PATH_EXP = /^(?:\\.*\\/)+/;\n\nexport const isBareSpecifier = specifier => ! PATH_EXP.test(specifier);\n\n/**\n * @typedef RouteMatch\n * @property {URLPatternResult|null} result The results of `pattern.exec(url)`\n * @property {string|null} specifier The module specifier mapped to the URL\n * @property {boolean} hasRegExpGroups\n * @readonly\n */\n\n/**\n * @type RouteMatch\n */\nconst invalidMatchResult = Object.freeze({ result: null, specifier: null, hasRegExpGroups: false });\n\n/**\n * Finds the URLPattern that corresponds to the given URL\n *\n * @param {string} url\n * @returns {URLPattern|undefined}\n */\nexport const getRegistryKey = url => reg.keys().find(pattern => pattern.test(url));\n\n/**\n *\n * @param {URLPattern} key\n * @returns {string|null} The module specifier\n */\nexport const getRegistrySpecifier = key => reg.get(key);\n\n/**\n *\n * @param {string} url\n * @returns {RouteMatch}\n */\nexport function lookupRoute(url) {\n\tif (cache.has(url)) {\n\t\treturn cache.get(url);\n\t} else {\n\t\tconst key = getRegistryKey(url);\n\n\t\tif (key instanceof URLPattern) {\n\t\t\tconst match = Object.freeze({\n\t\t\t\tresult: key.exec(url),\n\t\t\t\tspecifier: reg.get(key),\n\t\t\t\thasRegExpGroups: key.hasRegExpGroups,\n\t\t\t});\n\n\t\t\tcache.set(url, match);\n\t\t\treturn match;\n\t\t} else {\n\t\t\tcache.set(url, invalidMatchResult);\n\t\t\treturn invalidMatchResult;\n\t\t}\n\n\t}\n}\n\nfunction resolveSpecifier(specifier) {\n\tif (isBareSpecifier(specifier)) {\n\t\treturn import.meta.resolve(specifier);\n\t} else if (URL.canParse(specifier)) {\n\t\treturn specifier;\n\t} else if (specifier instanceof URL) {\n\t\treturn specifier.href;\n\t} else {\n\t\treturn new URL (specifier, document.baseURI).href;\n\t}\n}\n\n/**\n * Registers module `specifier` to handle routes matching `pattern`\n *\n * @param {string|URLPattern} pattern The pattern to handle\n * @param {string|URL} specifier The module to register to the pattern\n */\nexport function registerModule(pattern, specifier) {\n\tif (typeof specifier !== 'string' && ! (specifier instanceof URL)) {\n\t\tthrow new TypeError(`Invalid specifier type ${typeof specifier}.`);\n\t} else if (typeof pattern === 'string') {\n\t\treg.set(\n\t\t\tURL.canParse(pattern) ? new URLPattern(pattern) : new URLPattern({ pathname: pattern }),\n\t\t\tresolveSpecifier(specifier)\n\t\t);\n\t} else if (! (pattern instanceof URLPattern)) {\n\t\tthrow new TypeError(`Invalid pattner \"${pattern}\".`);\n\t} else {\n\t\treg.set(pattern, resolveSpecifier(specifier));\n\t}\n}\n","import { getRegistryKey, getRegistrySpecifier } from './routes.js';\n\nfunction _loadLink(href, {\n\trelList = [],\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'auto',\n\tsignal: passedSignal,\n\tas,\n\tintegrity,\n\tmedia,\n\ttype,\n} = {}) {\n\tconst { promise, resolve, reject } = Promise.withResolvers();\n\tconst link = document.createElement('link');\n\n\tif (passedSignal instanceof AbortSignal && passedSignal.aborted) {\n\t\treject(passedSignal.reason);\n\t} else if (typeof href !== 'string' && ! (href instanceof URL)) {\n\t\treject(new TypeError(`Invalid href to preload: \"${href}.`));\n\t} else {\n\t\tlink.relList.add(...relList);\n\n\t\tif (typeof fetchPriority === 'string') {\n\t\t\tlink.fetchPriority = fetchPriority;\n\t\t}\n\n\t\tif (typeof crossOrigin === 'string') {\n\t\t\tlink.crossOrigin = crossOrigin;\n\t\t}\n\n\t\tif (typeof type === 'string') {\n\t\t\tlink.type = type;\n\t\t}\n\n\t\tif (typeof media === 'string') {\n\t\t\tlink.media = media;\n\t\t} else if (media instanceof MediaQueryList) {\n\t\t\tlink.media = media.media;\n\t\t}\n\n\t\tif (typeof as === 'string') {\n\t\t\tlink.as = as;\n\t\t}\n\n\t\tif (typeof integrity === 'string') {\n\t\t\tlink.integrity = integrity;\n\t\t}\n\n\t\tif (link.relList.contains('preload') || link.relList.contains('modulepreload')) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst signal = passedSignal instanceof AbortSignal ? AbortSignal.any([controller.signal, passedSignal]) : controller.signal;\n\n\t\t\tif (passedSignal instanceof AbortSignal) {\n\t\t\t\tpassedSignal.addEventListener('abort', ({ target }) => {\n\t\t\t\t\treject(target.reason);\n\t\t\t\t}, { signal: controller.signal, once: true });\n\t\t\t}\n\n\t\t\tlink.referrerPolicy = referrerPolicy;\n\n\t\t\tlink.addEventListener('load', () => {\n\t\t\t\tresolve();\n\t\t\t\tcontroller.abort();\n\t\t\t}, { signal });\n\n\t\t\tlink.addEventListener('error', () => {\n\t\t\t\treject(new DOMException(`Error loading ${href}`, 'NotFoundError'));\n\t\t\t\tcontroller.abort();\n\t\t\t}, { signal });\n\n\t\t\tlink.href = href;\n\n\t\t\tdocument.head.append(link);\n\n\t\t\treturn promise.catch(reportError).finally(() => link.isConnected && link.remove());\n\t\t} else {\n\t\t\tlink.href = href;\n\t\t\tdocument.head.append(link);\n\t\t\tresolve();\n\t\t\treturn promise;\n\t\t}\n\t}\n}\n\nfunction _handlePreloadMutations(target) {\n\tif (target instanceof MutationRecord) {\n\t\t_handlePreloadMutations(target.target);\n\t} else if (target.tagName === 'A' && ! target.classList.contains('no-router')) {\n\t\tpreloadOnHover(target, target.dataset);\n\t} else {\n\t\ttarget.querySelectorAll('a:not(.no-router)').forEach(a => preloadOnHover(a, a.dataset));\n\t}\n}\n\nconst preloadObserver = new MutationObserver(entries => entries.forEach(_handlePreloadMutations));\n\n/**\n * Preloads a module asynchronously.\n *\n * @param {string} src - The URL or specifier to the module to preload.\n * @param {object} [options] - Optional options for the preload element.\n * @param {string} [options.crossOrigin=\"anonymous\"] - The CORS mode to use when fetching the module. Defaults to 'anonymous'.\n * @param {string} [options.referrerPolicy=\"no-referrer\"] - The referrer policy to use when fetching the module. Defaults to 'no-referrer'.\n * @param {string} [options.fetchPriority=\"low\"] - The fetch priority for the preload request. Defaults to 'auto'.\n * @param {string} [options.as=\"script\"] - The type of resource to preload. Defaults to 'script'.\n * @param {AbortSignal} [options.signal] - An AbortSignal to abort the preload request. Defaults to a 5-second timeout.\n * @param {string} [options.integrity] - A base64-encoded cryptographic hash of the resource\n * @returns {Promise<void>} A promise that resolves when the module is preloaded or rejects on error or signal is aborted.\n * @throws {Error} Throws if the signal is aborted or if an `error` event is fired on the preload.\n */\nexport async function preloadModule(src, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'low',\n\tas = 'script',\n\tsignal,\n\tintegrity,\n} = {}) {\n\tawait _loadLink(src, {\n\t\trelList: ['modulepreload'],\n\t\tcrossOrigin, referrerPolicy, fetchPriority, as, signal, integrity,\n\t});\n}\n\n/**\n * Preloads a resource asynchronously.\n\n * @param {string|URL} href - The URL or specifier to the resource to preload.\n * @param {Object} [options] - Optional options for the preload element.\n * @param {string} [options.crossOrigin=\"anonymous\"] - The CORS mode to use when fetching the resource. Defaults to 'anonymous'.\n * @param {string} [options.referrerPolicy=\"no-referrer\"] - The referrer policy to use when fetching the resource. Defaults to 'no-referrer'.\n * @param {string} [options.fetchPriority=\"auto\"] - The fetch priority for the preload request. Defaults to 'auto'.\n * @param {AbortSignal} [options.signal] - An AbortSignal to abort the preload request. Defaults to a 5-second timeout.\n * @param {string} [options.integrity] - A base64-encoded cryptographic hash of the resource\n * @param {string} [options.as] - The type of resource to preload.\n * @param {string} [options.type] - The MIME type of the resource to preload.\n * @param {(string|MediaQueryList)} [options.media] - A media query string or a MediaQueryList object.\n * @returns {Promise<void>} A promise that resolves when the resource is preloaded or rejects on error or signal is aborted.\n * @throws {Error} Throws if the signal is aborted or if an `error` event is fired on the preload.\n */\nexport async function preload(href, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'auto',\n\tsignal,\n\tas,\n\tintegrity,\n\tmedia,\n\ttype,\n} = {}) {\n\n\tawait _loadLink(href, {\n\t\trelList: ['preload'],\n\t\tcrossOrigin, referrerPolicy, fetchPriority, as, signal, type, media, integrity,\n\t});\n}\n/**\n * Preloads resources associated with an element or selector when hovered over, with optional configuration.\n *\n * @param {string|HTMLElement} target - A CSS selector string or an HTMLElement that triggers preloading.\n * @param {object} [options={}] - Configuration options for preloading.\n * @param {string} [options.crossOrigin='anonymous'] - The cross-origin attribute for the request, useful for fetching from other origins.\n * @param {string} [options.referrerPolicy='no-referrer'] - The referrer policy to apply to the request.\n * @param {string} [options.fetchPriority='high'] - The priority level of the fetch operation.\n * @param {AbortSignal} [options.signal] - Optional signal to abort the preload operation if needed.\n * @returns {Promise<void>} A promise that resolves once preloading completes.\n * @throws {TypeError} Throws if the target is not a valid selector or an HTMLElement with a valid `href` attribute.\n */\nexport async function preloadOnHover(target, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'high',\n\tsignal,\n} = {}) {\n\tconst { resolve, reject, promise } = Promise.withResolvers();\n\n\tif (typeof target === 'string') {\n\t\tawait Promise.all(Array.from(\n\t\t\tdocument.querySelectorAll(target),\n\t\t\tlink => preloadOnHover(link)\n\t\t)).then(resolve, reject);\n\t} else if (\n\t\ttarget instanceof HTMLElement\n\t\t&& ! target.classList.contains('no-router')\n\t\t&& typeof target.href === 'string'\n\t\t&& target.origin === location.origin\n\t\t&& target.download.length === 0\n\t\t&& URL.canParse(target.href)\n\t) {\n\t\ttarget.addEventListener('mouseover', async ({ currentTarget }) => {\n\t\t\tconst pattern = getRegistryKey(currentTarget.href);\n\n\t\t\tif (pattern instanceof URLPattern) {\n\t\t\t\tawait preloadModule(getRegistrySpecifier(pattern), {\n\t\t\t\t\tfetchPriority,\n\t\t\t\t\treferrerPolicy,\n\t\t\t\t\tcrossOrigin,\n\t\t\t\t\tintegrity: currentTarget.dataset.integrity,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\tawait preload(currentTarget.href, {\n\t\t\t\t\tfetchPriority,\n\t\t\t\t\tcrossOrigin,\n\t\t\t\t\treferrerPolicy,\n\t\t\t\t\tas: currentTarget.dataset.preloadAs ?? 'fetch',\n\t\t\t\t\ttype: currentTarget.dataset.preloadType ?? 'text/html',\n\t\t\t\t\tintegrity: currentTarget.dataset.integrity,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t}\n\t\t}, { once: true, passive: true, signal });\n\t} else {\n\t\tresolve();\n\t}\n\n\tawait promise;\n}\n\n/**\n * Adds `mouseenter` listeners to preload links/handlers via a `MutationObserver`\n *\n * @param {HTMLElement|ShadowRoot|string} target Target for the mutation observer or its selector\n * @param {HTMLElement|ShadowRoot} [base=document] The element to query from if `target` is a selector\n */\nexport function observePreloadsOn(target, base = document.documentElement) {\n\tif (typeof target === 'string') {\n\t\tobservePreloadsOn(base.querySelector(target));\n\t} else if (target instanceof HTMLElement || target instanceof ShadowRoot) {\n\t\tpreloadObserver.observe(target, { childList : true, subtree: true });\n\t\t_handlePreloadMutations(target);\n\t} else {\n\t\tthrow new TypeError('`observePreloadsOn` requires a selector or HTMLElement or ShadowRoot.');\n\t}\n}\n","import { registerModule, lookupRoute } from './routes.js';\nimport { observePreloadsOn } from './preload.js';\n\n/**\n * This is necessary since an HTML response from a same-origin\n * request should result in the same document state as if\n * it were initial load. CSP/Trusted Types requires `TrustedHTML`\n * for `Document.parseHTMLUnsage` (or `innerHTML`), and `setHTML()`\n * would filter out any `<iframe>` or `onclick` or `<form action>`.\n */\nconst policy = 'trustedTypes' in globalThis\n\t? trustedTypes.createPolicy('aegis-atlas#html', {\n\t\tcreateHTML(input) {\n\t\t\treturn input;\n\t\t}\n\t}) : Object.freeze({\n\t\tcreateHTML(input) {\n\t\t\treturn input;\n\t\t}\n\t});\n\nconst DESC_SELECTOR = 'meta[name=\"description\"], meta[itemprop=\"description\"], meta[property=\"og:description\"], meta[name=\"twitter:description\"]';\n\n/**\n * @typedef RouteContextObject\n * @property {URLPatternResult} result\n * @property {Record<string, string>} params\n * @property {DisposableStack} stack\n * @property {AbortController} controller\n * @property {AbortSignal} signal\n * @property {NavigationType} type\n * @property {URL} url\n * @property {any} state\n * @property {any} info\n * @property {number} timestamp\n * @readonly\n */\n\n/** @typedef {Response|DocumentFragment|Element|HTMLDocument|URL} HandlerResult */\n/** @typedef {(request: Request, context: RouteContextObject) => Promise<HandlerResult>} RouteHandler */\n\n/** @typedef {Readonly<Record<string, unknown>> & {default?: RouteHandler|HandlerResult, title?: string, description?: string, styles?: CSSStyleSheet|CSSStyleSheet[]}} Module */\n\n/**\n * @type HTMLElement\n */\nlet root = document.body;\n\n/**\n *\n * @param {string|HTMLElement} newRoot\n * @param {DocumentOrShadowRoot} base\n */\nexport function setRoot(newRoot, base = document) {\n\tif (typeof newRoot === 'string') {\n\t\tsetRoot(base.getElementById(newRoot));\n\t} else if (newRoot instanceof HTMLElement) {\n\t\troot = newRoot;\n\t} else {\n\t\tthrow new TypeError('New root must be an `Element` or `id` of an element.');\n\t}\n}\n\n/**\n *\n * @param {HTMLFormElement|HTMLButtonElement|HTMLAnchorElement} source\n * @returns {\"GET\"|\"POST\"}\n */\nfunction getRequestMethod(source) {\n\tif (! (source instanceof HTMLElement) || source instanceof HTMLAnchorElement) {\n\t\treturn 'GET';\n\t} else if (source instanceof HTMLFormElement) {\n\t\treturn source.method.toUpperCase();\n\t} else if (! (source instanceof HTMLButtonElement)) {\n\t\treturn 'GET';\n\t} else if (source.hasAttribute('formmethod') && source.formMethod.length !== 0) {\n\t\treturn source.formMethod.toUpperCase();\n\t} else if (source.form instanceof HTMLFormElement) {\n\t\treturn source.form.method.toUpperCase();\n\t} else {\n\t\tconsole.warn('Not sure this should be possible...');\n\t\treturn 'GET';\n\t}\n}\n\n/**\n *\n * @param {NavigationEvent} event\n */\nexport async function handleNavigation(event) {\n\tif (! (event instanceof NavigateEvent)) {\n\t\tthrow new TypeError('Not a navigation event.');\n\t} else if (event.signal.aborted) {\n\t\tthrow event.signal.reason;\n\t} else {\n\t\tconst method = getRequestMethod(event.sourceElement);\n\t\tconst request = new Request(event.destination.url, {\n\t\t\t// `sourceElement` could be a form, a `<button type=\"submit\">`, or an `<a>\n\t\t\tmethod: method,\n\t\t\tbody: method === 'GET' ? undefined : event.formData,// ?? new FormData(event.sourceElement?.form ?? event.sourceElement),\n\t\t\tsignal: event.signal,\n\t\t});\n\n\t\tconst { result, specifier, hasRegExpGroups } = lookupRoute(event.destination.url);\n\n\t\tif (typeof specifier !== 'string' || result === null) {\n\t\t\tconst resp = await fetch(request);\n\t\t\tawait updateContent(resp);\n\t\t} else {\n\t\t\tconst params = hasRegExpGroups ? {\n\t\t\t\t...result.protocol.groups, ...result.username.groups, ...result.password.groups, ...result.hostname.groups,\n\t\t\t\t...result.port.groups, ...result.pathname.groups, ...result.search.groups, ...result.hash.groups,\n\t\t\t}: {};\n\n\t\t\tdelete params['0'];\n\t\t\tconst module = await import(specifier);\n\t\t\tconst stack = new DisposableStack();\n\t\t\tconst controller = stack.adopt(\n\t\t\t\tnew AbortController(),\n\t\t\t\tcontroller => controller.abort(new DOMException('Stack was disposed.', 'AbortError')),\n\t\t\t);\n\n\t\t\tconst timestamp = performance.now();\n\t\t\tconst signal = AbortSignal.any([controller.signal, request.signal]);\n\n\t\t\t/**\n\t\t\t * @type {RouteContextObject}\n\t\t\t */\n\t\t\tconst context = Object.freeze({\n\t\t\t\ttimestamp,\n\t\t\t\tstack,\n\t\t\t\tcontroller,\n\t\t\t\ttype: event.navigationType,\n\t\t\t\tstate: event.destination.getState(),\n\t\t\t\tinfo: event.info,\n\t\t\t\turl: new URL(event.destination.url),\n\t\t\t\tsignal,\n\t\t\t\tresult,\n\t\t\t\tparams,\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\treturn await handleRequestModule(request, context, module);\n\t\t\t} catch(err) {\n\t\t\t\treportError(err);\n\t\t\t} finally {\n\t\t\t\tstack.dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n *\n * @param {unknown} routes\n * @param {object} config\n * @param {HTMLElement|string} [config.root]\n * @param {boolean} [config.preload=false]\n * @param {AbortSignal} [config.signal]\n */\nexport function init(routes, {\n\troot,\n\tpreload = false,\n\tsignal,\n} = {}) {\n\tif (typeof routes === 'string') {\n\t\tinit(JSON.parse(document.scripts.namedItem(routes).innerHTML), { root, preload, signal });\n\t} else if (typeof routes === 'number') {\n\t\tinit(JSON.parse(document.scripts.item(routes).innerHTML), { root, preload, signal });\n\t} else if (routes instanceof HTMLScriptElement) {\n\t\tinit(JSON.parse(routes.textContent), { root, preload, signal });\n\t} else if (typeof routes === 'object') {\n\t\tObject.entries(routes).forEach(([key, val]) => registerModule(key, val));\n\n\t\tif (typeof root === 'string' || root instanceof HTMLElement) {\n\t\t\tsetRoot(root);\n\t\t}\n\n\t\tnavigation.addEventListener('navigate', event => {\n\t\t\tif (event.canIntercept && event.destination.url.startsWith(location.origin) && ! event.sourceElement?.classList?.contains?.('no-router')) {\n\t\t\t\tevent.intercept({ handler: () => handleNavigation(event) });\n\t\t\t}\n\t\t}, { signal });\n\n\t\tif (preload) {\n\t\t\tobservePreloadsOn(document.body);\n\t\t}\n\t} else {\n\t\tthrow new TypeError(`Routes must be an object, \\`<script>\\`, or name/index of \\`document.scripts\\`. Got a ${typeof routes}.`);\n\t}\n}\n\n/**\n *\n * @param {object} options\n * @param {AbortSignal} [options.signal]\n * @returns {Promise<NavigationHistoryEntry>}\n */\nexport async function whenLoaded({ signal } = {}) {\n\tconst { resolve, reject, promise } = Promise.withResolvers();\n\n\tif (signal?.aborted) {\n\t\treject(signal.reason);\n\t} else {\n\t\tconst controller = new AbortController();\n\t\tconst opts = {\n\t\t\tonce: true,\n\t\t\tsignal: signal instanceof AbortSignal ? AbortSignal.any([signal, controller.signal]) : controller.signal,\n\t\t};\n\n\t\tnavigation.addEventListener('navigatesuccess', () => {\n\t\t\tresolve(navigation.currentEntry);\n\t\t\tcontroller.abort();\n\t\t}, opts);\n\n\t\tnavigation.addEventListener('navigateerror', event => {\n\t\t\treject(event.error);\n\t\t\tcontroller.abort();\n\t\t}, opts);\n\n\t\tif (signal instanceof AbortSignal) {\n\t\t\tsignal.addEventListener('abort', ({ target }) => {\n\t\t\t\treject(target.reason);\n\t\t\t\tcontroller.abort(target.reason);\n\t\t\t}, { once: true, signal: controller.signal });\n\t\t}\n\t}\n\n\treturn promise;\n}\n\n/**\n *\n * @param {string|URL} newURL\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const navigate = (newURL, options) => navigation.navigate(newURL, options);\n\n/**\n *\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const back = (options) => navigation.back(options);\n\n/**\n *\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const forward = (options) => navigation.forward(options);\n\n/**\n *\n * @param {NavigationReloadOptions} options\n * @returns {NavigationResult}\n */\nexport const reload = (options) => navigation.reload(options);\n\n/**\n *\n * @param {Request} request\n * @param {RouteContextObject} context\n * @param {Module} module\n */\nasync function handleRequestModule(request, context, module) {\n\tif (typeof module.default === 'undefined') {\n\t\tthrow new TypeError(`No default export in module for <${request.url}>.`);\n\t} else if (typeof module.default === 'function') {\n\t\tconst result = await module.default(request, context);\n\t\tawait updateContent(result);\n\t\tupdateMeta(module);\n\t} else {\n\t\tawait updateContent(module.default);\n\t\tupdateMeta(module);\n\t}\n}\n\nfunction updateMeta({ title, description, styles }) {\n\tif (typeof title === 'string') {\n\t\tdocument.title = title;\n\t}\n\n\tif (typeof description === 'string') {\n\t\tsetDescription(description);\n\t}\n\n\tif (styles instanceof CSSStyleSheet) {\n\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];\n\t} else if (Array.isArray(styles) && styles.length !== 0) {\n\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styles];\n\t}\n}\n\n/**\n *\n * @param {HandlerResult} content\n */\nasync function updateContent(content) {\n\tif (content instanceof URL) {\n\t\tnavigate(content);\n\t} else if (content instanceof Response) {\n\t\tif (! content.ok) {\n\t\t\tthrow new DOMException(`${content.url} [${content.status}]`, 'NetworkError');\n\t\t} else if (! content.headers.get('Content-Type')?.startsWith?.('text/html')) {\n\t\t\tthrow new TypeError(`Unsupported Content-Type for <${content.url}> - \"${content.headers.get('Content-Type') ?? 'Unset'}\".`);\n\t\t} else {\n\t\t\tconst html = await content.text();\n\t\t\t/** @type HTMLDocument */\n\t\t\tconst doc = Document.parseHTMLUnsafe(policy.createHTML(html)); // Unsafe, but necessary... Same-origin at least\n\t\t\tawait updateContent(doc);\n\t\t}\n\t} else if (content instanceof Element || content instanceof DocumentFragment) {\n\t\troot.replaceChildren(content);\n\t} else if (content instanceof HTMLDocument) {\n\t\tdocument.title = content.title;\n\t\tsetDescription(content.head.querySelector(DESC_SELECTOR)?.content);\n\n\t\tif (root instanceof HTMLBodyElement) {\n\t\t\troot.replaceChildren(...content.body.childNodes);\n\t\t} else if (root instanceof HTMLElement && typeof root.id === 'string') {\n\t\t\troot.replaceChildren(...content.getElementById(root.id)?.childNodes ?? []);\n\t\t} else {\n\t\t\tthrow new TypeError('Root must be `<body>` or an element with an `id`.');\n\t\t}\n\t} else {\n\t\tthrow new TypeError('Content must be an `Element`, `DocumentFragment`, `HTMLDocument`, or `Response`.');\n\t}\n}\n\n/**\n *\n * @param {string} description\n */\nfunction setDescription(description = '') {\n\tdocument.head.querySelectorAll(DESC_SELECTOR).forEach(el => el.content = description);\n}\n"],"names":["reg","Map","cache","PATH_EXP","invalidMatchResult","Object","freeze","result","specifier","hasRegExpGroups","getRegistryKey","url","keys","find","pattern","test","getRegistrySpecifier","key","get","lookupRoute","has","URLPattern","match","exec","set","resolveSpecifier","isBareSpecifier","resolve","URL","canParse","href","document","baseURI","registerModule","TypeError","pathname","_loadLink","relList","crossOrigin","referrerPolicy","fetchPriority","signal","passedSignal","as","integrity","media","type","promise","reject","Promise","withResolvers","link","createElement","AbortSignal","aborted","reason","add","MediaQueryList","contains","controller","AbortController","any","addEventListener","target","once","abort","DOMException","head","append","catch","reportError","finally","isConnected","remove","_handlePreloadMutations","MutationRecord","tagName","classList","querySelectorAll","forEach","a","preloadOnHover","dataset","preloadObserver","MutationObserver","entries","async","preloadModule","src","preload","all","Array","from","then","HTMLElement","origin","location","download","length","currentTarget","preloadAs","preloadType","passive","observePreloadsOn","base","documentElement","querySelector","ShadowRoot","observe","childList","subtree","policy","globalThis","trustedTypes","createPolicy","createHTML","input","DESC_SELECTOR","root","body","setRoot","newRoot","getElementById","handleNavigation","event","NavigateEvent","method","source","sourceElement","HTMLAnchorElement","HTMLFormElement","toUpperCase","HTMLButtonElement","hasAttribute","formMethod","form","console","warn","request","Request","destination","undefined","formData","resp","fetch","updateContent","params","protocol","groups","username","password","hostname","port","search","hash","module","import","stack","DisposableStack","adopt","timestamp","performance","now","context","navigationType","state","getState","info","default","updateMeta","handleRequestModule","err","dispose","init","routes","JSON","parse","scripts","namedItem","innerHTML","item","HTMLScriptElement","textContent","val","navigation","canIntercept","startsWith","intercept","handler","whenLoaded","opts","currentEntry","error","navigate","newURL","options","back","forward","reload","title","description","styles","setDescription","CSSStyleSheet","adoptedStyleSheets","isArray","content","Response","ok","status","headers","html","text","doc","Document","parseHTMLUnsafe","Element","DocumentFragment","replaceChildren","HTMLDocument","HTMLBodyElement","childNodes","id","el"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"atlas.min.js","sources":["routes.js","preload.js","router.js"],"sourcesContent":["/**\n * @type {Map<URLPattern, string>}\n */\nconst reg = new Map();\n\nconst cache = new Map();\n\nconst PATH_EXP = /^(?:\\.*\\/)+/;\n\nexport const isBareSpecifier = specifier => ! PATH_EXP.test(specifier);\n\n/**\n * @typedef RouteMatch\n * @property {URLPatternResult|null} result The results of `pattern.exec(url)`\n * @property {string|null} specifier The module specifier mapped to the URL\n * @property {boolean} hasRegExpGroups\n * @readonly\n */\n\n/**\n * @type RouteMatch\n */\nconst invalidMatchResult = Object.freeze({ result: null, specifier: null, hasRegExpGroups: false });\n\n/**\n * Finds the URLPattern that corresponds to the given URL\n *\n * @param {string} url\n * @returns {URLPattern|undefined}\n */\nexport const getRegistryKey = url => reg.keys().find(pattern => pattern.test(url));\n\n/**\n * Checks if a route for the given URL is registered\n * @param {string} [url=location.href]\n * @return {boolean}\n */\nexport const hasRegistryKey = (url = location.href) => reg.keys().some(pattern => pattern.test(url));\n\n/**\n *\n * @param {URLPattern} key\n * @returns {string|null} The module specifier\n */\nexport const getRegistrySpecifier = key => reg.get(key);\n\n/**\n *\n * @param {string} url\n * @returns {RouteMatch}\n */\nexport function lookupRoute(url) {\n\tif (cache.has(url)) {\n\t\treturn cache.get(url);\n\t} else {\n\t\tconst key = getRegistryKey(url);\n\n\t\tif (key instanceof URLPattern) {\n\t\t\tconst match = Object.freeze({\n\t\t\t\tresult: key.exec(url),\n\t\t\t\tspecifier: reg.get(key),\n\t\t\t\thasRegExpGroups: key.hasRegExpGroups,\n\t\t\t});\n\n\t\t\tcache.set(url, match);\n\t\t\treturn match;\n\t\t} else {\n\t\t\tcache.set(url, invalidMatchResult);\n\t\t\treturn invalidMatchResult;\n\t\t}\n\n\t}\n}\n\nfunction resolveSpecifier(specifier) {\n\tif (isBareSpecifier(specifier)) {\n\t\treturn import.meta.resolve(specifier);\n\t} else if (URL.canParse(specifier)) {\n\t\treturn specifier;\n\t} else if (specifier instanceof URL) {\n\t\treturn specifier.href;\n\t} else {\n\t\treturn new URL (specifier, document.baseURI).href;\n\t}\n}\n\n/**\n * Registers module `specifier` to handle routes matching `pattern`\n *\n * @param {string|URLPattern} pattern The pattern to handle\n * @param {string|URL} specifier The module to register to the pattern\n */\nexport function registerModule(pattern, specifier) {\n\tif (typeof specifier !== 'string' && ! (specifier instanceof URL)) {\n\t\tthrow new TypeError(`Invalid specifier type ${typeof specifier}.`);\n\t} else if (typeof pattern === 'string') {\n\t\treg.set(\n\t\t\tURL.canParse(pattern) ? new URLPattern(pattern) : new URLPattern({ pathname: pattern }),\n\t\t\tresolveSpecifier(specifier)\n\t\t);\n\t} else if (! (pattern instanceof URLPattern)) {\n\t\tthrow new TypeError(`Invalid pattner \"${pattern}\".`);\n\t} else {\n\t\treg.set(pattern, resolveSpecifier(specifier));\n\t}\n}\n","import { getRegistryKey, getRegistrySpecifier } from './routes.js';\n\nfunction _loadLink(href, {\n\trelList = [],\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'auto',\n\tsignal: passedSignal,\n\tas,\n\tintegrity,\n\tmedia,\n\ttype,\n} = {}) {\n\tconst { promise, resolve, reject } = Promise.withResolvers();\n\tconst link = document.createElement('link');\n\n\tif (passedSignal instanceof AbortSignal && passedSignal.aborted) {\n\t\treject(passedSignal.reason);\n\t} else if (typeof href !== 'string' && ! (href instanceof URL)) {\n\t\treject(new TypeError(`Invalid href to preload: \"${href}.`));\n\t} else {\n\t\tlink.relList.add(...relList);\n\n\t\tif (typeof fetchPriority === 'string') {\n\t\t\tlink.fetchPriority = fetchPriority;\n\t\t}\n\n\t\tif (typeof crossOrigin === 'string') {\n\t\t\tlink.crossOrigin = crossOrigin;\n\t\t}\n\n\t\tif (typeof type === 'string') {\n\t\t\tlink.type = type;\n\t\t}\n\n\t\tif (typeof media === 'string') {\n\t\t\tlink.media = media;\n\t\t} else if (media instanceof MediaQueryList) {\n\t\t\tlink.media = media.media;\n\t\t}\n\n\t\tif (typeof as === 'string') {\n\t\t\tlink.as = as;\n\t\t}\n\n\t\tif (typeof integrity === 'string') {\n\t\t\tlink.integrity = integrity;\n\t\t}\n\n\t\tif (link.relList.contains('preload') || link.relList.contains('modulepreload')) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst signal = passedSignal instanceof AbortSignal ? AbortSignal.any([controller.signal, passedSignal]) : controller.signal;\n\n\t\t\tif (passedSignal instanceof AbortSignal) {\n\t\t\t\tpassedSignal.addEventListener('abort', ({ target }) => {\n\t\t\t\t\treject(target.reason);\n\t\t\t\t}, { signal: controller.signal, once: true });\n\t\t\t}\n\n\t\t\tlink.referrerPolicy = referrerPolicy;\n\n\t\t\tlink.addEventListener('load', () => {\n\t\t\t\tresolve();\n\t\t\t\tcontroller.abort();\n\t\t\t}, { signal });\n\n\t\t\tlink.addEventListener('error', () => {\n\t\t\t\treject(new DOMException(`Error loading ${href}`, 'NotFoundError'));\n\t\t\t\tcontroller.abort();\n\t\t\t}, { signal });\n\n\t\t\tlink.href = href;\n\n\t\t\tdocument.head.append(link);\n\n\t\t\treturn promise.catch(reportError).finally(() => link.isConnected && link.remove());\n\t\t} else {\n\t\t\tlink.href = href;\n\t\t\tdocument.head.append(link);\n\t\t\tresolve();\n\t\t\treturn promise;\n\t\t}\n\t}\n}\n\nfunction _handlePreloadMutations(target) {\n\tif (target instanceof MutationRecord) {\n\t\t_handlePreloadMutations(target.target);\n\t} else if (target.tagName === 'A' && ! target.classList.contains('no-router')) {\n\t\tpreloadOnHover(target, target.dataset);\n\t} else {\n\t\ttarget.querySelectorAll('a:not(.no-router)').forEach(a => preloadOnHover(a, a.dataset));\n\t}\n}\n\nconst preloadObserver = new MutationObserver(entries => entries.forEach(_handlePreloadMutations));\n\n/**\n * Preloads a module asynchronously.\n *\n * @param {string} src - The URL or specifier to the module to preload.\n * @param {object} [options] - Optional options for the preload element.\n * @param {string} [options.crossOrigin=\"anonymous\"] - The CORS mode to use when fetching the module. Defaults to 'anonymous'.\n * @param {string} [options.referrerPolicy=\"no-referrer\"] - The referrer policy to use when fetching the module. Defaults to 'no-referrer'.\n * @param {string} [options.fetchPriority=\"low\"] - The fetch priority for the preload request. Defaults to 'auto'.\n * @param {string} [options.as=\"script\"] - The type of resource to preload. Defaults to 'script'.\n * @param {AbortSignal} [options.signal] - An AbortSignal to abort the preload request. Defaults to a 5-second timeout.\n * @param {string} [options.integrity] - A base64-encoded cryptographic hash of the resource\n * @returns {Promise<void>} A promise that resolves when the module is preloaded or rejects on error or signal is aborted.\n * @throws {Error} Throws if the signal is aborted or if an `error` event is fired on the preload.\n */\nexport async function preloadModule(src, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'low',\n\tas = 'script',\n\tsignal,\n\tintegrity,\n} = {}) {\n\tawait _loadLink(src, {\n\t\trelList: ['modulepreload'],\n\t\tcrossOrigin, referrerPolicy, fetchPriority, as, signal, integrity,\n\t});\n}\n\n/**\n * Preloads a resource asynchronously.\n\n * @param {string|URL} href - The URL or specifier to the resource to preload.\n * @param {Object} [options] - Optional options for the preload element.\n * @param {string} [options.crossOrigin=\"anonymous\"] - The CORS mode to use when fetching the resource. Defaults to 'anonymous'.\n * @param {string} [options.referrerPolicy=\"no-referrer\"] - The referrer policy to use when fetching the resource. Defaults to 'no-referrer'.\n * @param {string} [options.fetchPriority=\"auto\"] - The fetch priority for the preload request. Defaults to 'auto'.\n * @param {AbortSignal} [options.signal] - An AbortSignal to abort the preload request. Defaults to a 5-second timeout.\n * @param {string} [options.integrity] - A base64-encoded cryptographic hash of the resource\n * @param {string} [options.as] - The type of resource to preload.\n * @param {string} [options.type] - The MIME type of the resource to preload.\n * @param {(string|MediaQueryList)} [options.media] - A media query string or a MediaQueryList object.\n * @returns {Promise<void>} A promise that resolves when the resource is preloaded or rejects on error or signal is aborted.\n * @throws {Error} Throws if the signal is aborted or if an `error` event is fired on the preload.\n */\nexport async function preload(href, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'auto',\n\tsignal,\n\tas,\n\tintegrity,\n\tmedia,\n\ttype,\n} = {}) {\n\n\tawait _loadLink(href, {\n\t\trelList: ['preload'],\n\t\tcrossOrigin, referrerPolicy, fetchPriority, as, signal, type, media, integrity,\n\t});\n}\n/**\n * Preloads resources associated with an element or selector when hovered over, with optional configuration.\n *\n * @param {string|HTMLElement} target - A CSS selector string or an HTMLElement that triggers preloading.\n * @param {object} [options={}] - Configuration options for preloading.\n * @param {string} [options.crossOrigin='anonymous'] - The cross-origin attribute for the request, useful for fetching from other origins.\n * @param {string} [options.referrerPolicy='no-referrer'] - The referrer policy to apply to the request.\n * @param {string} [options.fetchPriority='high'] - The priority level of the fetch operation.\n * @param {AbortSignal} [options.signal] - Optional signal to abort the preload operation if needed.\n * @returns {Promise<void>} A promise that resolves once preloading completes.\n * @throws {TypeError} Throws if the target is not a valid selector or an HTMLElement with a valid `href` attribute.\n */\nexport async function preloadOnHover(target, {\n\tcrossOrigin = 'anonymous',\n\treferrerPolicy = 'no-referrer',\n\tfetchPriority = 'high',\n\tsignal,\n} = {}) {\n\tconst { resolve, reject, promise } = Promise.withResolvers();\n\n\tif (typeof target === 'string') {\n\t\tawait Promise.all(Array.from(\n\t\t\tdocument.querySelectorAll(target),\n\t\t\tlink => preloadOnHover(link)\n\t\t)).then(resolve, reject);\n\t} else if (\n\t\ttarget instanceof HTMLElement\n\t\t&& ! target.classList.contains('no-router')\n\t\t&& typeof target.href === 'string'\n\t\t&& target.origin === location.origin\n\t\t&& target.download.length === 0\n\t\t&& URL.canParse(target.href)\n\t) {\n\t\ttarget.addEventListener('mouseover', async ({ currentTarget }) => {\n\t\t\tconst pattern = getRegistryKey(currentTarget.href);\n\n\t\t\tif (pattern instanceof URLPattern) {\n\t\t\t\tawait preloadModule(getRegistrySpecifier(pattern), {\n\t\t\t\t\tfetchPriority,\n\t\t\t\t\treferrerPolicy,\n\t\t\t\t\tcrossOrigin,\n\t\t\t\t\tintegrity: currentTarget.dataset.integrity,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\tawait preload(currentTarget.href, {\n\t\t\t\t\tfetchPriority,\n\t\t\t\t\tcrossOrigin,\n\t\t\t\t\treferrerPolicy,\n\t\t\t\t\tas: currentTarget.dataset.preloadAs ?? 'fetch',\n\t\t\t\t\ttype: currentTarget.dataset.preloadType ?? 'text/html',\n\t\t\t\t\tintegrity: currentTarget.dataset.integrity,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t}\n\t\t}, { once: true, passive: true, signal });\n\t} else {\n\t\tresolve();\n\t}\n\n\tawait promise;\n}\n\n/**\n * Adds `mouseenter` listeners to preload links/handlers via a `MutationObserver`\n *\n * @param {HTMLElement|ShadowRoot|string} target Target for the mutation observer or its selector\n * @param {HTMLElement|ShadowRoot} [base=document] The element to query from if `target` is a selector\n */\nexport function observePreloadsOn(target, base = document.documentElement) {\n\tif (typeof target === 'string') {\n\t\tobservePreloadsOn(base.querySelector(target));\n\t} else if (target instanceof HTMLElement || target instanceof ShadowRoot) {\n\t\tpreloadObserver.observe(target, { childList : true, subtree: true });\n\t\t_handlePreloadMutations(target);\n\t} else {\n\t\tthrow new TypeError('`observePreloadsOn` requires a selector or HTMLElement or ShadowRoot.');\n\t}\n}\n","import { registerModule, lookupRoute, hasRegistryKey } from './routes.js';\nimport { observePreloadsOn } from './preload.js';\n\n/**\n * This is necessary since an HTML response from a same-origin\n * request should result in the same document state as if\n * it were initial load. CSP/Trusted Types requires `TrustedHTML`\n * for `Document.parseHTMLUnsage` (or `innerHTML`), and `setHTML()`\n * would filter out any `<iframe>` or `onclick` or `<form action>`.\n */\nconst policy = 'trustedTypes' in globalThis\n\t? trustedTypes.createPolicy('aegis-atlas#html', {\n\t\tcreateHTML(input) {\n\t\t\treturn input;\n\t\t}\n\t}) : Object.freeze({\n\t\tcreateHTML(input) {\n\t\t\treturn input;\n\t\t}\n\t});\n\nconst DESC_SELECTOR = 'meta[name=\"description\"], meta[itemprop=\"description\"], meta[property=\"og:description\"], meta[name=\"twitter:description\"]';\n\n/**\n * @typedef RouteContextObject\n * @property {URLPatternResult} result\n * @property {Record<string, string>} params\n * @property {DisposableStack} stack\n * @property {AbortController} controller\n * @property {AbortSignal} signal\n * @property {NavigationType} type\n * @property {URL} newURL\n * @property {URL} oldURL\n * @property {any} state\n * @property {any} info\n * @property {number} timestamp\n * @readonly\n */\n\n/** @typedef {Response|DocumentFragment|Element|HTMLDocument|URL} HandlerResult */\n/** @typedef {(request: Request, context: RouteContextObject) => Promise<HandlerResult>} RouteHandler */\n\n/** @typedef {Readonly<Record<string, unknown>> & {default?: RouteHandler|HandlerResult, title?: string, description?: string, styles?: CSSStyleSheet|CSSStyleSheet[]}} Module */\n\n/**\n * @type HTMLElement\n */\nlet root = document.body;\n\n/**\n *\n * @param {string|HTMLElement} newRoot\n * @param {DocumentOrShadowRoot} base\n */\nexport function setRoot(newRoot, base = document) {\n\tif (typeof newRoot === 'string') {\n\t\tsetRoot(base.getElementById(newRoot));\n\t} else if (newRoot instanceof HTMLElement) {\n\t\troot = newRoot;\n\t} else {\n\t\tthrow new TypeError('New root must be an `Element` or `id` of an element.');\n\t}\n}\n\n/**\n *\n * @param {HTMLFormElement|HTMLButtonElement|HTMLAnchorElement} source\n * @returns {\"GET\"|\"POST\"}\n */\nfunction getRequestMethod(source) {\n\tif (! (source instanceof HTMLElement) || source instanceof HTMLAnchorElement) {\n\t\treturn 'GET';\n\t} else if (source instanceof HTMLFormElement) {\n\t\treturn source.method.toUpperCase();\n\t} else if (! (source instanceof HTMLButtonElement)) {\n\t\treturn 'GET';\n\t} else if (source.hasAttribute('formmethod') && source.formMethod.length !== 0) {\n\t\treturn source.formMethod.toUpperCase();\n\t} else if (source.form instanceof HTMLFormElement) {\n\t\treturn source.form.method.toUpperCase();\n\t} else {\n\t\tconsole.warn('Not sure this should be possible...');\n\t\treturn 'GET';\n\t}\n}\n\n/**\n *\n * @param {NavigationEvent} event\n * @param {URL} oldURL\n */\nexport async function handleNavigation(event, oldURL = new URL(location.href)) {\n\tif (! (event instanceof NavigateEvent)) {\n\t\tthrow new TypeError('Not a navigation event.');\n\t} else if (event.signal.aborted) {\n\t\tthrow event.signal.reason;\n\t} else {\n\t\tconst method = getRequestMethod(event.sourceElement);\n\t\tconst request = new Request(event.destination.url, {\n\t\t\t// `sourceElement` could be a form, a `<button type=\"submit\">`, or an `<a>\n\t\t\tmethod: method,\n\t\t\tbody: method === 'GET' ? undefined : event.formData,\n\t\t\tsignal: event.signal,\n\t\t\tmode: 'same-origin',\n\t\t\tcredentials: 'include',\n\t\t\tpriority: 'high',\n\t\t});\n\n\t\tconst { result, specifier, hasRegExpGroups } = lookupRoute(event.destination.url);\n\n\t\tif (typeof specifier !== 'string' || result === null) {\n\t\t\tconst resp = await fetch(request);\n\t\t\tawait updateContent(resp, {});\n\t\t} else {\n\t\t\tconst params = hasRegExpGroups ? {\n\t\t\t\t...result.protocol.groups, ...result.username.groups, ...result.password.groups, ...result.hostname.groups,\n\t\t\t\t...result.port.groups, ...result.pathname.groups, ...result.search.groups, ...result.hash.groups,\n\t\t\t}: {};\n\n\t\t\tdelete params['0'];\n\t\t\tconst module = await import(specifier);\n\t\t\tconst stack = new DisposableStack();\n\t\t\tconst controller = stack.adopt(\n\t\t\t\tnew AbortController(),\n\t\t\t\tcontroller => controller.abort(new DOMException('Stack was disposed.', 'AbortError')),\n\t\t\t);\n\n\t\t\tconst timestamp = performance.now();\n\t\t\tconst signal = AbortSignal.any([controller.signal, request.signal]);\n\n\t\t\t/**\n\t\t\t * Dispose of stack on next navigation or on error, triggering clean-up\n\t\t\t */\n\t\t\tnavigation.addEventListener('navigate', () => stack.dispose(), { once: true, signal });\n\t\t\tnavigation.addEventListener('navigateerror', () => stack.dispose(), { once: true, signal });\n\n\t\t\t/**\n\t\t\t * @type {RouteContextObject}\n\t\t\t */\n\t\t\tconst context = Object.freeze({\n\t\t\t\ttimestamp,\n\t\t\t\tstack,\n\t\t\t\tcontroller,\n\t\t\t\ttype: event.navigationType,\n\t\t\t\tstate: event.destination.getState(),\n\t\t\t\tinfo: event.info,\n\t\t\t\tnewURL: new URL(event.destination.url),\n\t\t\t\toldURL,\n\t\t\t\tsignal,\n\t\t\t\tresult,\n\t\t\t\tparams,\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\treturn await handleRequestModule(request, context, module);\n\t\t\t} catch(err) {\n\t\t\t\treportError(err);\n\t\t\t\tstack.dispose(); // Ensure clean-up happens even in an error\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n *\n * @param {unknown} routes\n * @param {object} config\n * @param {HTMLElement|string} [config.root]\n * @param {boolean} [config.preload=false]\n * @param {AbortSignal} [config.signal]\n */\nexport function init(routes, {\n\troot,\n\tpreload = false,\n\tsignal,\n} = {}) {\n\tif (typeof routes === 'string') {\n\t\tinit(JSON.parse(document.scripts.namedItem(routes).innerHTML), { root, preload, signal });\n\t} else if (typeof routes === 'number') {\n\t\tinit(JSON.parse(document.scripts.item(routes).innerHTML), { root, preload, signal });\n\t} else if (routes instanceof HTMLScriptElement) {\n\t\tinit(JSON.parse(routes.textContent), { root, preload, signal });\n\t} else if (typeof routes === 'object') {\n\t\tObject.entries(routes).forEach(([key, val]) => registerModule(key, val));\n\n\t\tif (typeof root === 'string' || root instanceof HTMLElement) {\n\t\t\tsetRoot(root);\n\t\t}\n\n\t\tnavigation.addEventListener('navigate', event => {\n\t\t\tconst oldURL = new URL(location.href);\n\t\t\tif (event.canIntercept && event.destination.url.startsWith(location.origin) && ! event.sourceElement?.classList?.contains?.('no-router')) {\n\t\t\t\tevent.intercept({ handler: () => handleNavigation(event, oldURL) });\n\t\t\t}\n\t\t}, { signal });\n\n\t\tif (preload) {\n\t\t\tobservePreloadsOn(document.body);\n\t\t}\n\n\t\tif (hasRegistryKey(location.href)) {\n\t\t\tnavigation.reload({ info: 'Initial Load', state: navigation.currentEntry.getState() });\n\t\t}\n\t} else {\n\t\tthrow new TypeError(`Routes must be an object, \\`<script>\\`, or name/index of \\`document.scripts\\`. Got a ${typeof routes}.`);\n\t}\n}\n\n/**\n *\n * @param {object} options\n * @param {AbortSignal} [options.signal]\n * @returns {Promise<NavigationHistoryEntry>}\n */\nexport async function whenLoaded({ signal } = {}) {\n\tconst { resolve, reject, promise } = Promise.withResolvers();\n\n\tif (signal?.aborted) {\n\t\treject(signal.reason);\n\t} else {\n\t\tconst controller = new AbortController();\n\t\tconst opts = {\n\t\t\tonce: true,\n\t\t\tsignal: signal instanceof AbortSignal ? AbortSignal.any([signal, controller.signal]) : controller.signal,\n\t\t};\n\n\t\tnavigation.addEventListener('navigatesuccess', () => {\n\t\t\tresolve(navigation.currentEntry);\n\t\t\tcontroller.abort();\n\t\t}, opts);\n\n\t\tnavigation.addEventListener('navigateerror', event => {\n\t\t\treject(event.error);\n\t\t\tcontroller.abort();\n\t\t}, opts);\n\n\t\tif (signal instanceof AbortSignal) {\n\t\t\tsignal.addEventListener('abort', ({ target }) => {\n\t\t\t\treject(target.reason);\n\t\t\t\tcontroller.abort(target.reason);\n\t\t\t}, { once: true, signal: controller.signal });\n\t\t}\n\t}\n\n\treturn promise;\n}\n\n/**\n *\n * @param {string|URL} newURL\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const navigate = (newURL, options) => navigation.navigate(newURL, options);\n\n/**\n *\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const back = (options) => navigation.back(options);\n\n/**\n *\n * @param {NavigationOptions} options\n * @returns {NavigationResult}\n */\nexport const forward = (options) => navigation.forward(options);\n\n/**\n *\n * @param {NavigationReloadOptions} options\n * @returns {NavigationResult}\n */\nexport const reload = (options) => navigation.reload(options);\n\n/**\n *\n * @param {Request} request\n * @param {RouteContextObject} context\n * @param {Module} module\n */\nasync function handleRequestModule(request, context, module) {\n\tif (typeof module.default === 'undefined') {\n\t\tthrow new TypeError(`No default export in module for <${request.url}>.`);\n\t} else if (typeof module.default === 'function') {\n\t\tconst result = module.default.prototype instanceof HTMLElement\n\t\t\t? new module.default(request, context)\n\t\t\t: await module.default(request, context);\n\n\t\tawait updateContent(result, module);\n\t\tupdateMeta(module, context);\n\t} else {\n\t\tawait updateContent(module.default);\n\t\tupdateMeta(module, context);\n\t}\n}\n\nasync function updateMeta({ title, description, styles }, context) {\n\tif (typeof title === 'string') {\n\t\tdocument.title = title;\n\t} else if (typeof title === 'function') {\n\t\tdocument.title = await title(context);\n\t}\n\n\tif (typeof description === 'string') {\n\t\tsetDescription(description);\n\t} else if (typeof description === 'function') {\n\t\tsetDescription(await description(context));\n\t}\n\n\tif (styles instanceof CSSStyleSheet) {\n\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];\n\t} else if (Array.isArray(styles) && styles.length !== 0) {\n\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styles];\n\t} else if (typeof styles === 'function') {\n\t\tconst newStyles = await styles(context);\n\t\tif (newStyles instanceof CSSStyleSheet) {\n\t\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, newStyles];\n\t\t} else if (Array.isArray(newStyles) && newStyles.length !== 0) {\n\t\t\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, ...newStyles];\n\t\t}\n\t}\n}\n\n/**\n *\n * @param {HandlerResult} content\n */\nasync function updateContent(content, { viewTransitionTypes: types = [] } = {}) {\n\tif (content instanceof URL) {\n\t\tnavigate(content);\n\t} else if (content instanceof Response) {\n\t\tif (! content.ok) {\n\t\t\tthrow new DOMException(`${content.url} [${content.status}]`, 'NetworkError');\n\t\t} else if (! content.headers.get('Content-Type')?.startsWith?.('text/html')) {\n\t\t\tthrow new TypeError(`Unsupported Content-Type for <${content.url}> - \"${content.headers.get('Content-Type') ?? 'Unset'}\".`);\n\t\t} else {\n\t\t\tconst html = await content.text();\n\t\t\t/** @type HTMLDocument */\n\t\t\tconst doc = Document.parseHTMLUnsafe(policy.createHTML(html)); // Unsafe, but necessary... Same-origin at least\n\t\t\tawait updateContent(doc, { viewTransitionTypes: types });\n\t\t}\n\t} else if (content instanceof Element || content instanceof DocumentFragment) {\n\t\tawait document.startViewTransition({\n\t\t\ttypes,\n\t\t\tupdate() {\n\t\t\t\troot.replaceChildren(content);\n\t\t\t},\n\t\t});\n\t} else if (content instanceof HTMLDocument) {\n\t\tdocument.title = content.title;\n\t\tsetDescription(content.head.querySelector(DESC_SELECTOR)?.content);\n\n\t\tif (root instanceof HTMLBodyElement) {\n\t\t\tawait document.startViewTransition({\n\t\t\t\ttypes,\n\t\t\t\tupdate() {\n\t\t\t\t\troot.replaceChildren(...content.body.childNodes);\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (root instanceof HTMLElement && typeof root.id === 'string') {\n\t\t\tawait document.startViewTransition({\n\t\t\t\ttypes,\n\t\t\t\tupdate() {\n\t\t\t\t\troot.replaceChildren(...content.getElementById(root.id)?.childNodes ?? []);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tthrow new TypeError('Root must be `<body>` or an element with an `id`.');\n\t\t}\n\t} else {\n\t\tthrow new TypeError('Content must be an `Element`, `DocumentFragment`, `HTMLDocument`, or `Response`.');\n\t}\n}\n\n/**\n *\n * @param {string} description\n */\nfunction setDescription(description = '') {\n\tdocument.head.querySelectorAll(DESC_SELECTOR).forEach(el => el.content = description);\n}\n"],"names":["reg","Map","cache","PATH_EXP","invalidMatchResult","Object","freeze","result","specifier","hasRegExpGroups","getRegistryKey","url","keys","find","pattern","test","getRegistrySpecifier","key","get","lookupRoute","has","URLPattern","match","exec","set","resolveSpecifier","isBareSpecifier","resolve","URL","canParse","href","document","baseURI","registerModule","TypeError","pathname","_loadLink","relList","crossOrigin","referrerPolicy","fetchPriority","signal","passedSignal","as","integrity","media","type","promise","reject","Promise","withResolvers","link","createElement","AbortSignal","aborted","reason","add","MediaQueryList","contains","controller","AbortController","any","addEventListener","target","once","abort","DOMException","head","append","catch","reportError","finally","isConnected","remove","_handlePreloadMutations","MutationRecord","tagName","classList","querySelectorAll","forEach","a","preloadOnHover","dataset","preloadObserver","MutationObserver","entries","async","preloadModule","src","preload","all","Array","from","then","HTMLElement","origin","location","download","length","currentTarget","preloadAs","preloadType","passive","observePreloadsOn","base","documentElement","querySelector","ShadowRoot","observe","childList","subtree","policy","globalThis","trustedTypes","createPolicy","createHTML","input","DESC_SELECTOR","root","body","setRoot","newRoot","getElementById","handleNavigation","event","oldURL","NavigateEvent","method","source","sourceElement","HTMLAnchorElement","HTMLFormElement","toUpperCase","HTMLButtonElement","hasAttribute","formMethod","form","console","warn","request","Request","destination","undefined","formData","mode","credentials","priority","resp","fetch","updateContent","params","protocol","groups","username","password","hostname","port","search","hash","module","import","stack","DisposableStack","adopt","timestamp","performance","now","navigation","dispose","context","navigationType","state","getState","info","newURL","default","prototype","updateMeta","handleRequestModule","err","init","routes","JSON","parse","scripts","namedItem","innerHTML","item","HTMLScriptElement","textContent","val","canIntercept","startsWith","intercept","handler","some","hasRegistryKey","reload","currentEntry","whenLoaded","opts","error","navigate","options","back","forward","title","description","styles","setDescription","CSSStyleSheet","adoptedStyleSheets","isArray","newStyles","content","viewTransitionTypes","types","Response","ok","status","headers","html","text","doc","Document","parseHTMLUnsafe","Element","DocumentFragment","startViewTransition","update","replaceChildren","HTMLDocument","HTMLBodyElement","childNodes","id","el"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aegisjsproject/atlas",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A client-side router library using `Navigation` & `URLPattern`",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"router",
|
|
@@ -85,13 +85,16 @@
|
|
|
85
85
|
},
|
|
86
86
|
"homepage": "https://github.com/AegisJSProject/atlas#readme",
|
|
87
87
|
"devDependencies": {
|
|
88
|
-
"@aegisjsproject/dev-server": "^1.0
|
|
89
|
-
"@aegisjsproject/http-utils": "^1.0
|
|
88
|
+
"@aegisjsproject/dev-server": "^1.1.0",
|
|
89
|
+
"@aegisjsproject/http-utils": "^1.1.0",
|
|
90
90
|
"@rollup/plugin-terser": "^1.0.0",
|
|
91
|
-
"@shgysk8zer0/eslint-config": "^1.0
|
|
92
|
-
"@shgysk8zer0/http-server": "^1.
|
|
93
|
-
"@shgysk8zer0/importmap": "^1.
|
|
94
|
-
"eslint": "^10.
|
|
95
|
-
"rollup": "^4.
|
|
91
|
+
"@shgysk8zer0/eslint-config": "^1.1.0",
|
|
92
|
+
"@shgysk8zer0/http-server": "^1.2.0",
|
|
93
|
+
"@shgysk8zer0/importmap": "^1.10.2",
|
|
94
|
+
"eslint": "^10.5.0",
|
|
95
|
+
"rollup": "^4.62.2"
|
|
96
|
+
},
|
|
97
|
+
"allowScripts": {
|
|
98
|
+
"fsevents@2.3.3": true
|
|
96
99
|
}
|
|
97
100
|
}
|
package/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { registerModule, lookupRoute } from './routes.js';
|
|
1
|
+
import { registerModule, lookupRoute, hasRegistryKey } from './routes.js';
|
|
2
2
|
import { observePreloadsOn } from './preload.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -29,7 +29,8 @@ const DESC_SELECTOR = 'meta[name="description"], meta[itemprop="description"], m
|
|
|
29
29
|
* @property {AbortController} controller
|
|
30
30
|
* @property {AbortSignal} signal
|
|
31
31
|
* @property {NavigationType} type
|
|
32
|
-
* @property {URL}
|
|
32
|
+
* @property {URL} newURL
|
|
33
|
+
* @property {URL} oldURL
|
|
33
34
|
* @property {any} state
|
|
34
35
|
* @property {any} info
|
|
35
36
|
* @property {number} timestamp
|
|
@@ -86,8 +87,9 @@ function getRequestMethod(source) {
|
|
|
86
87
|
/**
|
|
87
88
|
*
|
|
88
89
|
* @param {NavigationEvent} event
|
|
90
|
+
* @param {URL} oldURL
|
|
89
91
|
*/
|
|
90
|
-
export async function handleNavigation(event) {
|
|
92
|
+
export async function handleNavigation(event, oldURL = new URL(location.href)) {
|
|
91
93
|
if (! (event instanceof NavigateEvent)) {
|
|
92
94
|
throw new TypeError('Not a navigation event.');
|
|
93
95
|
} else if (event.signal.aborted) {
|
|
@@ -97,15 +99,18 @@ export async function handleNavigation(event) {
|
|
|
97
99
|
const request = new Request(event.destination.url, {
|
|
98
100
|
// `sourceElement` could be a form, a `<button type="submit">`, or an `<a>
|
|
99
101
|
method: method,
|
|
100
|
-
body: method === 'GET' ? undefined : event.formData
|
|
102
|
+
body: method === 'GET' ? undefined : event.formData,
|
|
101
103
|
signal: event.signal,
|
|
104
|
+
mode: 'same-origin',
|
|
105
|
+
credentials: 'include',
|
|
106
|
+
priority: 'high',
|
|
102
107
|
});
|
|
103
108
|
|
|
104
109
|
const { result, specifier, hasRegExpGroups } = lookupRoute(event.destination.url);
|
|
105
110
|
|
|
106
111
|
if (typeof specifier !== 'string' || result === null) {
|
|
107
112
|
const resp = await fetch(request);
|
|
108
|
-
await updateContent(resp);
|
|
113
|
+
await updateContent(resp, {});
|
|
109
114
|
} else {
|
|
110
115
|
const params = hasRegExpGroups ? {
|
|
111
116
|
...result.protocol.groups, ...result.username.groups, ...result.password.groups, ...result.hostname.groups,
|
|
@@ -123,6 +128,12 @@ export async function handleNavigation(event) {
|
|
|
123
128
|
const timestamp = performance.now();
|
|
124
129
|
const signal = AbortSignal.any([controller.signal, request.signal]);
|
|
125
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Dispose of stack on next navigation or on error, triggering clean-up
|
|
133
|
+
*/
|
|
134
|
+
navigation.addEventListener('navigate', () => stack.dispose(), { once: true, signal });
|
|
135
|
+
navigation.addEventListener('navigateerror', () => stack.dispose(), { once: true, signal });
|
|
136
|
+
|
|
126
137
|
/**
|
|
127
138
|
* @type {RouteContextObject}
|
|
128
139
|
*/
|
|
@@ -133,7 +144,8 @@ export async function handleNavigation(event) {
|
|
|
133
144
|
type: event.navigationType,
|
|
134
145
|
state: event.destination.getState(),
|
|
135
146
|
info: event.info,
|
|
136
|
-
|
|
147
|
+
newURL: new URL(event.destination.url),
|
|
148
|
+
oldURL,
|
|
137
149
|
signal,
|
|
138
150
|
result,
|
|
139
151
|
params,
|
|
@@ -143,8 +155,7 @@ export async function handleNavigation(event) {
|
|
|
143
155
|
return await handleRequestModule(request, context, module);
|
|
144
156
|
} catch(err) {
|
|
145
157
|
reportError(err);
|
|
146
|
-
|
|
147
|
-
stack.dispose();
|
|
158
|
+
stack.dispose(); // Ensure clean-up happens even in an error
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
161
|
}
|
|
@@ -177,14 +188,19 @@ export function init(routes, {
|
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
navigation.addEventListener('navigate', event => {
|
|
191
|
+
const oldURL = new URL(location.href);
|
|
180
192
|
if (event.canIntercept && event.destination.url.startsWith(location.origin) && ! event.sourceElement?.classList?.contains?.('no-router')) {
|
|
181
|
-
event.intercept({ handler: () => handleNavigation(event) });
|
|
193
|
+
event.intercept({ handler: () => handleNavigation(event, oldURL) });
|
|
182
194
|
}
|
|
183
195
|
}, { signal });
|
|
184
196
|
|
|
185
197
|
if (preload) {
|
|
186
198
|
observePreloadsOn(document.body);
|
|
187
199
|
}
|
|
200
|
+
|
|
201
|
+
if (hasRegistryKey(location.href)) {
|
|
202
|
+
navigation.reload({ info: 'Initial Load', state: navigation.currentEntry.getState() });
|
|
203
|
+
}
|
|
188
204
|
} else {
|
|
189
205
|
throw new TypeError(`Routes must be an object, \`<script>\`, or name/index of \`document.scripts\`. Got a ${typeof routes}.`);
|
|
190
206
|
}
|
|
@@ -268,28 +284,42 @@ async function handleRequestModule(request, context, module) {
|
|
|
268
284
|
if (typeof module.default === 'undefined') {
|
|
269
285
|
throw new TypeError(`No default export in module for <${request.url}>.`);
|
|
270
286
|
} else if (typeof module.default === 'function') {
|
|
271
|
-
const result =
|
|
272
|
-
|
|
273
|
-
|
|
287
|
+
const result = module.default.prototype instanceof HTMLElement
|
|
288
|
+
? new module.default(request, context)
|
|
289
|
+
: await module.default(request, context);
|
|
290
|
+
|
|
291
|
+
await updateContent(result, module);
|
|
292
|
+
updateMeta(module, context);
|
|
274
293
|
} else {
|
|
275
294
|
await updateContent(module.default);
|
|
276
|
-
updateMeta(module);
|
|
295
|
+
updateMeta(module, context);
|
|
277
296
|
}
|
|
278
297
|
}
|
|
279
298
|
|
|
280
|
-
function updateMeta({ title, description, styles }) {
|
|
299
|
+
async function updateMeta({ title, description, styles }, context) {
|
|
281
300
|
if (typeof title === 'string') {
|
|
282
301
|
document.title = title;
|
|
302
|
+
} else if (typeof title === 'function') {
|
|
303
|
+
document.title = await title(context);
|
|
283
304
|
}
|
|
284
305
|
|
|
285
306
|
if (typeof description === 'string') {
|
|
286
307
|
setDescription(description);
|
|
308
|
+
} else if (typeof description === 'function') {
|
|
309
|
+
setDescription(await description(context));
|
|
287
310
|
}
|
|
288
311
|
|
|
289
312
|
if (styles instanceof CSSStyleSheet) {
|
|
290
313
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
|
|
291
314
|
} else if (Array.isArray(styles) && styles.length !== 0) {
|
|
292
315
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styles];
|
|
316
|
+
} else if (typeof styles === 'function') {
|
|
317
|
+
const newStyles = await styles(context);
|
|
318
|
+
if (newStyles instanceof CSSStyleSheet) {
|
|
319
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, newStyles];
|
|
320
|
+
} else if (Array.isArray(newStyles) && newStyles.length !== 0) {
|
|
321
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...newStyles];
|
|
322
|
+
}
|
|
293
323
|
}
|
|
294
324
|
}
|
|
295
325
|
|
|
@@ -297,7 +327,7 @@ function updateMeta({ title, description, styles }) {
|
|
|
297
327
|
*
|
|
298
328
|
* @param {HandlerResult} content
|
|
299
329
|
*/
|
|
300
|
-
async function updateContent(content) {
|
|
330
|
+
async function updateContent(content, { viewTransitionTypes: types = [] } = {}) {
|
|
301
331
|
if (content instanceof URL) {
|
|
302
332
|
navigate(content);
|
|
303
333
|
} else if (content instanceof Response) {
|
|
@@ -309,18 +339,33 @@ async function updateContent(content) {
|
|
|
309
339
|
const html = await content.text();
|
|
310
340
|
/** @type HTMLDocument */
|
|
311
341
|
const doc = Document.parseHTMLUnsafe(policy.createHTML(html)); // Unsafe, but necessary... Same-origin at least
|
|
312
|
-
await updateContent(doc);
|
|
342
|
+
await updateContent(doc, { viewTransitionTypes: types });
|
|
313
343
|
}
|
|
314
344
|
} else if (content instanceof Element || content instanceof DocumentFragment) {
|
|
315
|
-
|
|
345
|
+
await document.startViewTransition({
|
|
346
|
+
types,
|
|
347
|
+
update() {
|
|
348
|
+
root.replaceChildren(content);
|
|
349
|
+
},
|
|
350
|
+
});
|
|
316
351
|
} else if (content instanceof HTMLDocument) {
|
|
317
352
|
document.title = content.title;
|
|
318
353
|
setDescription(content.head.querySelector(DESC_SELECTOR)?.content);
|
|
319
354
|
|
|
320
355
|
if (root instanceof HTMLBodyElement) {
|
|
321
|
-
|
|
356
|
+
await document.startViewTransition({
|
|
357
|
+
types,
|
|
358
|
+
update() {
|
|
359
|
+
root.replaceChildren(...content.body.childNodes);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
322
362
|
} else if (root instanceof HTMLElement && typeof root.id === 'string') {
|
|
323
|
-
|
|
363
|
+
await document.startViewTransition({
|
|
364
|
+
types,
|
|
365
|
+
update() {
|
|
366
|
+
root.replaceChildren(...content.getElementById(root.id)?.childNodes ?? []);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
324
369
|
} else {
|
|
325
370
|
throw new TypeError('Root must be `<body>` or an element with an `id`.');
|
|
326
371
|
}
|
package/routes.js
CHANGED
|
@@ -30,6 +30,13 @@ const invalidMatchResult = Object.freeze({ result: null, specifier: null, hasReg
|
|
|
30
30
|
*/
|
|
31
31
|
export const getRegistryKey = url => reg.keys().find(pattern => pattern.test(url));
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a route for the given URL is registered
|
|
35
|
+
* @param {string} [url=location.href]
|
|
36
|
+
* @return {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export const hasRegistryKey = (url = location.href) => reg.keys().some(pattern => pattern.test(url));
|
|
39
|
+
|
|
33
40
|
/**
|
|
34
41
|
*
|
|
35
42
|
* @param {URLPattern} key
|