@braidlabs/core 0.6.0 → 0.7.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/package.json +1 -1
- package/src/adapters/adapter.d.ts +11 -0
- package/src/adapters/adapter.js.map +1 -1
- package/src/adapters/compat-adapter.js +2 -2
- package/src/adapters/compat-adapter.js.map +1 -1
- package/src/compat/born-inert-scripts.d.ts +2 -0
- package/src/compat/born-inert-scripts.js +39 -0
- package/src/compat/born-inert-scripts.js.map +1 -1
- package/src/compat/cookie-partition.d.ts +25 -0
- package/src/compat/cookie-partition.js +79 -0
- package/src/compat/cookie-partition.js.map +1 -0
- package/src/compat/document-member-classification.js +3 -2
- package/src/compat/document-member-classification.js.map +1 -1
- package/src/compat/document-overrides.d.ts +6 -6
- package/src/compat/document-overrides.js +23 -0
- package/src/compat/document-overrides.js.map +1 -1
- package/src/compat/fragment-boundary.d.ts +5 -0
- package/src/compat/fragment-boundary.js +159 -12
- package/src/compat/fragment-boundary.js.map +1 -1
- package/src/compat/html-policy.d.ts +50 -0
- package/src/compat/html-policy.js +155 -0
- package/src/compat/html-policy.js.map +1 -0
- package/src/compat/realm-patches.d.ts +2 -1
- package/src/compat/realm-patches.js +117 -3
- package/src/compat/realm-patches.js.map +1 -1
- package/src/compat/script-execution.d.ts +24 -0
- package/src/compat/script-execution.js +177 -10
- package/src/compat/script-execution.js.map +1 -1
- package/src/compat/storage-partition.d.ts +47 -0
- package/src/compat/storage-partition.js +291 -0
- package/src/compat/storage-partition.js.map +1 -0
- package/src/compat/subresource-urls.d.ts +41 -0
- package/src/compat/subresource-urls.js +133 -0
- package/src/compat/subresource-urls.js.map +1 -0
- package/src/config.d.ts +28 -0
- package/src/config.js +24 -0
- package/src/config.js.map +1 -1
- package/src/elements/boot-gate.d.ts +47 -0
- package/src/elements/boot-gate.js +207 -0
- package/src/elements/boot-gate.js.map +1 -0
- package/src/elements/fragment-slot.d.ts +34 -0
- package/src/elements/fragment-slot.js +282 -24
- package/src/elements/fragment-slot.js.map +1 -1
- package/src/elements/late-content.d.ts +44 -0
- package/src/elements/late-content.js +107 -0
- package/src/elements/late-content.js.map +1 -0
- package/src/elements/prefetch.d.ts +27 -0
- package/src/elements/prefetch.js +100 -0
- package/src/elements/prefetch.js.map +1 -0
- package/src/elements/prerequisites.d.ts +42 -0
- package/src/elements/prerequisites.js +133 -0
- package/src/elements/prerequisites.js.map +1 -0
- package/src/elements/recovery.d.ts +46 -0
- package/src/elements/recovery.js +59 -0
- package/src/elements/recovery.js.map +1 -0
- package/src/errors.d.ts +4 -1
- package/src/errors.js +2 -0
- package/src/errors.js.map +1 -1
- package/src/index.d.ts +4 -1
- package/src/index.js +10 -1
- package/src/index.js.map +1 -1
- package/src/realm/realm-manager.js +23 -0
- package/src/realm/realm-manager.js.map +1 -1
- package/src/weave/capabilities.d.ts +35 -7
- package/src/weave/capabilities.js +17 -0
- package/src/weave/capabilities.js.map +1 -1
package/src/config.d.ts
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* names itself, which trusted fragments may then read and write.
|
|
9
9
|
*/
|
|
10
10
|
import type { HostContract } from './weave/contract.js';
|
|
11
|
+
import { type RecoveryPolicy } from './elements/recovery.js';
|
|
11
12
|
import type { FragmentCapabilities } from './weave/capabilities.js';
|
|
13
|
+
import type { PrerequisiteResolver } from './elements/prerequisites.js';
|
|
12
14
|
export interface BraidOptions {
|
|
13
15
|
/**
|
|
14
16
|
* What this host offers fragments, as a contract they can require a range of.
|
|
@@ -91,6 +93,29 @@ export interface BraidOptions {
|
|
|
91
93
|
* fragment documents from `/manage/__braid/…`. Call `initBraid` before any slot connects.
|
|
92
94
|
*/
|
|
93
95
|
basePath?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Resolvers for mount prerequisites: named things that must be ready before a fragment's code
|
|
98
|
+
* runs, such as a warm session for its authorization server. Keyed by exact key or by a
|
|
99
|
+
* `prefix*` pattern. A fragment declares the keys it needs with `<fragment-slot requires="…">`
|
|
100
|
+
* or its manifest's `requires`. See `elements/prerequisites.ts`.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* initBraid({ prerequisites: { 'session:*': (key, { signal }) => sessions.ensure(key, { signal }) } });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
prerequisites?: Record<string, PrerequisiteResolver>;
|
|
107
|
+
/** How long a prerequisite may take before its fragment fails. Defaults to 15 seconds. */
|
|
108
|
+
prerequisiteTimeoutMs?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Reboot a fragment that fails for a reason a retry can fix — its fetch, its realm, its
|
|
111
|
+
* handshake or mount failed, or it stopped answering (`gone`) — up to three times per mount, after
|
|
112
|
+
* 1, 4 and 16 seconds. Off by default. `true` takes those defaults; an object sets them. See
|
|
113
|
+
* `elements/recovery.ts` for what is and is not retried.
|
|
114
|
+
*/
|
|
115
|
+
recover?: boolean | {
|
|
116
|
+
attempts?: number;
|
|
117
|
+
delayMs?: number;
|
|
118
|
+
};
|
|
94
119
|
}
|
|
95
120
|
/** See {@link BraidOptions.realmGlobals}. */
|
|
96
121
|
export type RealmGlobalsHook = (fragment: {
|
|
@@ -106,6 +131,9 @@ interface ResolvedBraidConfig {
|
|
|
106
131
|
sharedGlobals?: string[];
|
|
107
132
|
realmGlobals?: RealmGlobalsHook;
|
|
108
133
|
onHostNavigation?: (notify: () => void) => void;
|
|
134
|
+
prerequisites?: Record<string, PrerequisiteResolver>;
|
|
135
|
+
prerequisiteTimeoutMs?: number;
|
|
136
|
+
recover?: RecoveryPolicy;
|
|
109
137
|
}
|
|
110
138
|
export declare function setBraidConfig(options?: BraidOptions): void;
|
|
111
139
|
export declare function getBraidConfig(): Readonly<ResolvedBraidConfig>;
|
package/src/config.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* own realm and shadow DOM subtree. The one channel back is `sharedGlobals`: host globals the host
|
|
8
8
|
* names itself, which trusted fragments may then read and write.
|
|
9
9
|
*/
|
|
10
|
+
import { DEFAULT_RECOVERY, MAX_RECOVERY_ATTEMPTS } from './elements/recovery.js';
|
|
10
11
|
const config = {
|
|
11
12
|
dev: false,
|
|
12
13
|
basePath: '',
|
|
@@ -30,6 +31,29 @@ export function setBraidConfig(options = {}) {
|
|
|
30
31
|
if (options.onHostNavigation) {
|
|
31
32
|
config.onHostNavigation = options.onHostNavigation;
|
|
32
33
|
}
|
|
34
|
+
if (options.prerequisites) {
|
|
35
|
+
config.prerequisites = options.prerequisites;
|
|
36
|
+
}
|
|
37
|
+
if (options.prerequisiteTimeoutMs !== undefined) {
|
|
38
|
+
if (!Number.isFinite(options.prerequisiteTimeoutMs) || options.prerequisiteTimeoutMs <= 0) {
|
|
39
|
+
throw new Error(`braid: prerequisiteTimeoutMs must be a positive number of milliseconds`);
|
|
40
|
+
}
|
|
41
|
+
config.prerequisiteTimeoutMs = options.prerequisiteTimeoutMs;
|
|
42
|
+
}
|
|
43
|
+
if (options.recover !== undefined) {
|
|
44
|
+
if (options.recover === false) {
|
|
45
|
+
delete config.recover;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const given = options.recover === true ? {} : options.recover;
|
|
49
|
+
const attempts = given.attempts ?? DEFAULT_RECOVERY.attempts;
|
|
50
|
+
const delayMs = given.delayMs ?? DEFAULT_RECOVERY.delayMs;
|
|
51
|
+
if (!Number.isInteger(attempts) || attempts < 1 || attempts > MAX_RECOVERY_ATTEMPTS || !Number.isFinite(delayMs) || delayMs < 0) {
|
|
52
|
+
throw new Error(`braid: recover needs 1 to ${MAX_RECOVERY_ATTEMPTS} attempts and a delayMs of zero or more`);
|
|
53
|
+
}
|
|
54
|
+
config.recover = { attempts, delayMs };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
33
57
|
// Kept in sync with the gateway's normalizeBasePath: a path the URL parser would rewrite (`/café`,
|
|
34
58
|
// a dot segment) could never match the parsed pathnames the gateway compares it to.
|
|
35
59
|
if (options.basePath !== undefined) {
|
package/src/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../libs/core/src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../libs/core/src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a slot boots its fragment: at once (`eager`, the default), or on intent.
|
|
3
|
+
*
|
|
4
|
+
* - `idle` — once the page has loaded and the browser next has nothing better to do, or two
|
|
5
|
+
* seconds after load regardless.
|
|
6
|
+
* - `visible` — when the slot comes within one viewport of being on screen.
|
|
7
|
+
* - `interaction` — on the first hover, pointer, key, or focus inside a pierced slot. The content
|
|
8
|
+
* is already painted, so the user has something to interact with; the fragment's code is what
|
|
9
|
+
* waits. A slot that was not pierced has nothing on screen to interact with, so it boots when
|
|
10
|
+
* visible instead.
|
|
11
|
+
*
|
|
12
|
+
* Deferring a boot defers all of it — the realm iframe, the document fetch, the fragment's
|
|
13
|
+
* framework and app — which is more than lazy hydration can defer inside one framework.
|
|
14
|
+
*/
|
|
15
|
+
export type BootMode = 'eager' | 'idle' | 'visible' | 'interaction';
|
|
16
|
+
export declare function parseBootMode(value: string | null, fragmentId: string): BootMode;
|
|
17
|
+
export interface BootGate {
|
|
18
|
+
/** Resolves when the slot should boot. Never rejects; never resolves after `dispose()`. */
|
|
19
|
+
readonly triggered: Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Re-dispatches the clicks and submits that arrived before the fragment could handle them, on
|
|
22
|
+
* their original targets, one per task, and stops holding. Called once the fragment is ready.
|
|
23
|
+
*/
|
|
24
|
+
replay(): void;
|
|
25
|
+
/** Stops waiting and holding, and drops anything held — the fragment will not be handling it. */
|
|
26
|
+
dispose(): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Waits for a slot's boot trigger, holding what the user did in the meantime.
|
|
30
|
+
*
|
|
31
|
+
* Clicks and submits inside the slot are the events whose default action goes wrong without the
|
|
32
|
+
* fragment's code — a link followed, a form posted — so until the fragment is ready they are
|
|
33
|
+
* cancelled, kept, and replayed. At most one per element is kept, so three anxious clicks on "Pay"
|
|
34
|
+
* become one. Every other event is left alone, and listened to passively: typed text and focus are
|
|
35
|
+
* already in the DOM, which the fragment adopts in place.
|
|
36
|
+
*
|
|
37
|
+
* A replay is a synthetic event, some time after the real one, so what needs a fresh user gesture —
|
|
38
|
+
* `window.open`, a file picker, the clipboard — will not work from it.
|
|
39
|
+
*
|
|
40
|
+
* The listeners are on the slot's own shadow root, never on the host's `window` or `document`. The
|
|
41
|
+
* one exception is `idle`, which waits for the host window's `load` once, if the page has not
|
|
42
|
+
* loaded yet.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createBootGate(slot: HTMLElement, mode: BootMode, options: {
|
|
45
|
+
pierced: boolean;
|
|
46
|
+
fragmentId: string;
|
|
47
|
+
}): BootGate;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { isDevMode } from '../config.js';
|
|
2
|
+
const BOOT_MODES = ['eager', 'idle', 'visible', 'interaction'];
|
|
3
|
+
/** Events that start an `interaction` boot. Only `click` and `submit` are ever cancelled. */
|
|
4
|
+
const PASSIVE_INTENT_EVENTS = ['pointerover', 'pointerdown', 'touchstart', 'keydown', 'focusin'];
|
|
5
|
+
const HELD_EVENTS = ['click', 'submit'];
|
|
6
|
+
/** Held at most this many, so a user hammering a dead button cannot queue an avalanche. */
|
|
7
|
+
const MAX_HELD = 10;
|
|
8
|
+
/**
|
|
9
|
+
* How far outside the viewport a `visible` slot starts booting. A boot is a network round trip and
|
|
10
|
+
* an iframe, so it needs more lead than an image does; a smaller margin shows the skeleton on any
|
|
11
|
+
* brisk scroll.
|
|
12
|
+
*/
|
|
13
|
+
const VISIBLE_MARGIN = '100%';
|
|
14
|
+
/** The longest an `idle` boot waits, after load, for the browser to go idle. */
|
|
15
|
+
const IDLE_TIMEOUT_MS = 2_000;
|
|
16
|
+
export function parseBootMode(value, fragmentId) {
|
|
17
|
+
if (value === null || value === '')
|
|
18
|
+
return 'eager';
|
|
19
|
+
if (BOOT_MODES.includes(value))
|
|
20
|
+
return value;
|
|
21
|
+
console.warn(`[braid:${fragmentId}] unknown boot="${value}", booting eagerly; expected one of ${BOOT_MODES.join(', ')}`);
|
|
22
|
+
return 'eager';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Waits for a slot's boot trigger, holding what the user did in the meantime.
|
|
26
|
+
*
|
|
27
|
+
* Clicks and submits inside the slot are the events whose default action goes wrong without the
|
|
28
|
+
* fragment's code — a link followed, a form posted — so until the fragment is ready they are
|
|
29
|
+
* cancelled, kept, and replayed. At most one per element is kept, so three anxious clicks on "Pay"
|
|
30
|
+
* become one. Every other event is left alone, and listened to passively: typed text and focus are
|
|
31
|
+
* already in the DOM, which the fragment adopts in place.
|
|
32
|
+
*
|
|
33
|
+
* A replay is a synthetic event, some time after the real one, so what needs a fresh user gesture —
|
|
34
|
+
* `window.open`, a file picker, the clipboard — will not work from it.
|
|
35
|
+
*
|
|
36
|
+
* The listeners are on the slot's own shadow root, never on the host's `window` or `document`. The
|
|
37
|
+
* one exception is `idle`, which waits for the host window's `load` once, if the page has not
|
|
38
|
+
* loaded yet.
|
|
39
|
+
*/
|
|
40
|
+
export function createBootGate(slot, mode, options) {
|
|
41
|
+
const held = [];
|
|
42
|
+
let stopTrigger = () => undefined;
|
|
43
|
+
let stopHolding = () => undefined;
|
|
44
|
+
let done = false;
|
|
45
|
+
let fire;
|
|
46
|
+
const triggered = new Promise((resolve) => {
|
|
47
|
+
fire = () => {
|
|
48
|
+
if (done)
|
|
49
|
+
return;
|
|
50
|
+
stopTrigger();
|
|
51
|
+
stopTrigger = () => undefined;
|
|
52
|
+
resolve();
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
const root = slot.shadowRoot;
|
|
56
|
+
const effective = mode === 'interaction' && !(options.pierced && root) ? 'visible' : mode;
|
|
57
|
+
if (effective === 'eager') {
|
|
58
|
+
fire();
|
|
59
|
+
}
|
|
60
|
+
else if (effective === 'idle') {
|
|
61
|
+
let cancel = () => undefined;
|
|
62
|
+
const whenIdle = () => {
|
|
63
|
+
if (typeof requestIdleCallback === 'function') {
|
|
64
|
+
const handle = requestIdleCallback(() => fire(), { timeout: IDLE_TIMEOUT_MS });
|
|
65
|
+
cancel = () => cancelIdleCallback(handle);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Safari has no requestIdleCallback: a moment after load is the closest equivalent
|
|
69
|
+
const handle = setTimeout(() => fire(), 200);
|
|
70
|
+
cancel = () => clearTimeout(handle);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
// idle during page load is time the page's own content needs
|
|
74
|
+
if (document.readyState === 'complete')
|
|
75
|
+
whenIdle();
|
|
76
|
+
else {
|
|
77
|
+
addEventListener('load', whenIdle, { once: true });
|
|
78
|
+
cancel = () => removeEventListener('load', whenIdle);
|
|
79
|
+
}
|
|
80
|
+
stopTrigger = () => cancel();
|
|
81
|
+
}
|
|
82
|
+
else if (effective === 'visible') {
|
|
83
|
+
if (typeof IntersectionObserver !== 'function') {
|
|
84
|
+
fire();
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const observer = new IntersectionObserver((entries) => {
|
|
88
|
+
if (entries.some((entry) => entry.isIntersecting))
|
|
89
|
+
fire();
|
|
90
|
+
}, { rootMargin: VISIBLE_MARGIN });
|
|
91
|
+
observer.observe(slot);
|
|
92
|
+
stopTrigger = () => observer.disconnect();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const shadow = root;
|
|
97
|
+
// After the next paint, so the boot's work does not land in the frame that answers the
|
|
98
|
+
// interaction that started it — which is the frame INP measures.
|
|
99
|
+
const fireAfterPaint = () => {
|
|
100
|
+
if (typeof requestAnimationFrame === 'function')
|
|
101
|
+
requestAnimationFrame(() => setTimeout(fire, 0));
|
|
102
|
+
else
|
|
103
|
+
setTimeout(fire, 0);
|
|
104
|
+
};
|
|
105
|
+
const onIntent = () => fireAfterPaint();
|
|
106
|
+
const onHeld = (event) => {
|
|
107
|
+
hold(event);
|
|
108
|
+
fireAfterPaint();
|
|
109
|
+
};
|
|
110
|
+
for (const type of PASSIVE_INTENT_EVENTS)
|
|
111
|
+
shadow.addEventListener(type, onIntent, { capture: true, passive: true });
|
|
112
|
+
// Kept until replay, not only until the trigger: a click while the fragment boots needs
|
|
113
|
+
// holding as much as the first.
|
|
114
|
+
for (const type of HELD_EVENTS)
|
|
115
|
+
shadow.addEventListener(type, onHeld, { capture: true });
|
|
116
|
+
stopHolding = () => {
|
|
117
|
+
for (const type of PASSIVE_INTENT_EVENTS)
|
|
118
|
+
shadow.removeEventListener(type, onIntent, { capture: true });
|
|
119
|
+
for (const type of HELD_EVENTS)
|
|
120
|
+
shadow.removeEventListener(type, onHeld, { capture: true });
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function hold(event) {
|
|
124
|
+
const target = event.composedPath()[0];
|
|
125
|
+
if (!target)
|
|
126
|
+
return;
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
// The fragment's own handlers do not exist yet, and the host's bubbling listeners should see
|
|
129
|
+
// this action once — when it is replayed — rather than once cancelled and again for real.
|
|
130
|
+
event.stopPropagation();
|
|
131
|
+
if (held.length >= MAX_HELD || held.some((entry) => entry.target === target))
|
|
132
|
+
return;
|
|
133
|
+
held.push(event.type === 'submit'
|
|
134
|
+
? { target, submitter: event.submitter ?? null }
|
|
135
|
+
: { target, click: mouseInit(event) });
|
|
136
|
+
}
|
|
137
|
+
function finish() {
|
|
138
|
+
done = true;
|
|
139
|
+
stopTrigger();
|
|
140
|
+
stopHolding();
|
|
141
|
+
}
|
|
142
|
+
function replayOne(entry) {
|
|
143
|
+
if (!entry.target.isConnected) {
|
|
144
|
+
if (isDevMode()) {
|
|
145
|
+
console.warn(`[braid:${options.fragmentId}] an action taken before boot was dropped: its element left the ` +
|
|
146
|
+
`DOM while the fragment booted, so the fragment re-rendered instead of adopting its markup`);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
if (entry.click) {
|
|
152
|
+
entry.target.dispatchEvent(new MouseEvent('click', entry.click));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const form = entry.target;
|
|
156
|
+
// a submitter the fragment replaced is no longer this form's, and requestSubmit would throw
|
|
157
|
+
const submitter = entry.submitter?.isConnected && entry.submitter.form === form ? entry.submitter : undefined;
|
|
158
|
+
form.requestSubmit(submitter);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.warn(`[braid:${options.fragmentId}] replaying an action taken before boot failed`, error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
triggered,
|
|
167
|
+
replay() {
|
|
168
|
+
if (done)
|
|
169
|
+
return;
|
|
170
|
+
finish();
|
|
171
|
+
const queue = held.splice(0);
|
|
172
|
+
// One per task, so each handler's own re-render — a button disabling itself — lands first.
|
|
173
|
+
const next = () => {
|
|
174
|
+
const entry = queue.shift();
|
|
175
|
+
if (!entry)
|
|
176
|
+
return;
|
|
177
|
+
replayOne(entry);
|
|
178
|
+
if (queue.length)
|
|
179
|
+
setTimeout(next, 0);
|
|
180
|
+
};
|
|
181
|
+
next();
|
|
182
|
+
},
|
|
183
|
+
dispose() {
|
|
184
|
+
finish();
|
|
185
|
+
held.length = 0;
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function mouseInit(event) {
|
|
190
|
+
return {
|
|
191
|
+
bubbles: true,
|
|
192
|
+
cancelable: true,
|
|
193
|
+
composed: true,
|
|
194
|
+
detail: event.detail,
|
|
195
|
+
clientX: event.clientX,
|
|
196
|
+
clientY: event.clientY,
|
|
197
|
+
screenX: event.screenX,
|
|
198
|
+
screenY: event.screenY,
|
|
199
|
+
button: event.button,
|
|
200
|
+
buttons: event.buttons,
|
|
201
|
+
ctrlKey: event.ctrlKey,
|
|
202
|
+
shiftKey: event.shiftKey,
|
|
203
|
+
altKey: event.altKey,
|
|
204
|
+
metaKey: event.metaKey,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=boot-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-gate.js","sourceRoot":"","sources":["../../../../../libs/core/src/elements/boot-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,11 @@ import { LivenessState } from '../weave/liveness.js';
|
|
|
17
17
|
export type FragmentSlotState = 'idle' | 'loading' | 'ready' | 'error' | 'closing' | 'healthy' | 'unobservable' | 'suspect' | 'gone';
|
|
18
18
|
/** `connecting` is reported as `ready`; every other liveness state carries its own name. */
|
|
19
19
|
export declare function slotStateFor(liveness: LivenessState): FragmentSlotState;
|
|
20
|
+
/**
|
|
21
|
+
* Whether a slot sits in another fragment's DOM: some shadow root above it belongs to a slot. A
|
|
22
|
+
* host's own components may keep slots in their shadow roots; those are the host's.
|
|
23
|
+
*/
|
|
24
|
+
export declare function insideAnotherFragment(slot: Element): boolean;
|
|
20
25
|
/**
|
|
21
26
|
* Finds server-rendered fragment content the gateway pierced into a slot.
|
|
22
27
|
*
|
|
@@ -35,6 +40,12 @@ export declare function findPiercedContentRoot(shadowRoot: ShadowRoot | null): H
|
|
|
35
40
|
* than a moment of unresponsiveness — and suspicion is designed to be recoverable.
|
|
36
41
|
*/
|
|
37
42
|
export declare const SLOT_STYLES: string;
|
|
43
|
+
/**
|
|
44
|
+
* Adds the slot a host's `slot="loading"` child projects into. Only on roots the client builds: a
|
|
45
|
+
* pierced fragment already has its content on screen. (After `reload()` a pierced slot's root is
|
|
46
|
+
* rebuilt by the client, and can show its loading state like any other.)
|
|
47
|
+
*/
|
|
48
|
+
export declare function ensureLoadingSlot(shadowRoot: ShadowRoot): void;
|
|
38
49
|
/**
|
|
39
50
|
* Ensures the shadow root can present host-provided fallback content:
|
|
40
51
|
*
|
|
@@ -59,6 +70,8 @@ export declare function ensureFallbackSlot(shadowRoot: ShadowRoot): void;
|
|
|
59
70
|
* - `src` — optional route url for the fragment. When absent, the fragment is *bound*: it
|
|
60
71
|
* follows the host page's location and participates in host navigation.
|
|
61
72
|
* - `props` — JSON attribute (or the `props` property) passed to the fragment instance.
|
|
73
|
+
* - `boot` — `eager` (default), `idle`, `visible`, or `interaction`: when to boot. Read when the
|
|
74
|
+
* slot connects, or when a deferred slot's late markup arrives. See `boot-gate.ts`.
|
|
62
75
|
*
|
|
63
76
|
* Events: `braid:ready`, `braid:error` (detail includes stage + fix hint), `braid:event`
|
|
64
77
|
* (fragment → host).
|
|
@@ -90,6 +103,27 @@ export declare class FragmentSlot extends HTMLElement {
|
|
|
90
103
|
disconnectedCallback(): void;
|
|
91
104
|
reload(): Promise<void>;
|
|
92
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Starts loading a fragment before its slot exists — from a link's hover, or a router's preload
|
|
108
|
+
* hook — so a navigation that is about to mount it finds the work already in flight.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* link.addEventListener('pointerenter', () => prefetchFragment('billing', '/billing/invoices'));
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* `route` is what the slot will render: the destination page's path for a routed fragment, or the
|
|
115
|
+
* slot's `src` for a widget. Two things start:
|
|
116
|
+
*
|
|
117
|
+
* - the fragment's document, held for the next slot of that fragment and route to mount within
|
|
118
|
+
* ten seconds (`PREFETCH_TTL_MS`) — so that slot makes no document request of its own;
|
|
119
|
+
* - the scripts the document names, in its own namespace, as `<link rel="prefetch">` the realm
|
|
120
|
+
* then loads from the HTTP cache.
|
|
121
|
+
*
|
|
122
|
+
* Compat fragments only: contract and untrusted fragments load no Braid document. Calling it again
|
|
123
|
+
* for the same fragment and route while a prefetch is held does nothing. Call
|
|
124
|
+
* `clearPrefetchedFragments()` when the user signs out.
|
|
125
|
+
*/
|
|
126
|
+
export declare function prefetchFragment(fragmentId: string, route: string): void;
|
|
93
127
|
/**
|
|
94
128
|
* Fetches the fragment's document from the gateway's document namespace.
|
|
95
129
|
*
|