@microsoft/webui-framework 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -53
- package/dist/component-asset/asset.d.ts +4 -2
- package/dist/component-asset/asset.js +19 -3
- package/dist/component-asset/generated-manifest.d.ts +1 -0
- package/dist/component-asset/generated-manifest.js +32 -0
- package/dist/component-asset/loader.d.ts +2 -1
- package/dist/component-asset/loader.js +98 -22
- package/dist/component-asset/manifest.d.ts +2 -1
- package/dist/component-asset.d.ts +1 -1
- package/dist/component-asset.js +12 -2
- package/dist/element/link-styles.d.ts +20 -0
- package/dist/element/link-styles.js +1030 -0
- package/dist/element/markers.d.ts +4 -0
- package/dist/element/markers.js +60 -5
- package/dist/element/styles.d.ts +39 -1
- package/dist/element/styles.js +623 -13
- package/dist/element/types.d.ts +5 -3
- package/dist/element.js +9 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/interaction-hydration.d.ts +8 -0
- package/dist/interaction-hydration.js +167 -0
- package/dist/static-host.js +43 -1
- package/dist/streaming-activation.d.ts +2 -2
- package/dist/streaming-activation.js +4 -2
- package/dist/streaming-bootstrap.d.ts +2 -2
- package/dist/streaming-bootstrap.js +7 -1
- package/dist/streaming-cleanup.d.ts +1 -0
- package/dist/streaming-cleanup.js +66 -14
- package/dist/streaming-coordinator.d.ts +3 -2
- package/dist/streaming-coordinator.js +107 -47
- package/dist/streaming-deferred.d.ts +14 -4
- package/dist/streaming-deferred.js +200 -87
- package/dist/streaming-dom.d.ts +12 -3
- package/dist/streaming-dom.js +41 -27
- package/dist/streaming-mode.d.ts +5 -0
- package/dist/streaming-mode.js +4 -0
- package/dist/streaming-protocol.d.ts +19 -3
- package/dist/streaming-protocol.js +4 -3
- package/dist/streaming-spans.d.ts +9 -0
- package/dist/streaming-spans.js +200 -0
- package/dist/template-content.d.ts +17 -0
- package/dist/template-content.js +106 -0
- package/dist/template-element.d.ts +17 -2
- package/dist/template-element.js +493 -177
- package/dist/template-events.d.ts +7 -0
- package/dist/template-events.js +7 -1
- package/dist/template-types.d.ts +1 -2
- package/dist/template.d.ts +15 -8
- package/dist/template.js +79 -16
- package/package.json +9 -3
- package/dist/component-asset/resources.d.ts +0 -4
- package/dist/component-asset/resources.js +0 -83
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export { WebUIElement } from './element.js';
|
|
|
2
2
|
export { observable, attr } from './decorators.js';
|
|
3
3
|
export { getTemplate, registerTemplateData } from './template.js';
|
|
4
4
|
export type { TemplateMeta } from './template.js';
|
|
5
|
+
export { installComponentStyles, prepareComponentStyles, registerComponentStyles, } from './element/styles.js';
|
|
6
|
+
export type { ComponentStyleResource, ComponentStyles, } from './element/styles.js';
|
|
5
7
|
export { hydrationStart, hydrationEnd } from './lifecycle.js';
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,5 @@ setTimeout(installTemplateElementRuntime, 0);
|
|
|
3
3
|
export { WebUIElement } from './element.js';
|
|
4
4
|
export { observable, attr } from './decorators.js';
|
|
5
5
|
export { getTemplate, registerTemplateData } from './template.js';
|
|
6
|
+
export { installComponentStyles, prepareComponentStyles, registerComponentStyles, } from './element/styles.js';
|
|
6
7
|
export { hydrationStart, hydrationEnd } from './lifecycle.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface InteractionHydrationOptions {
|
|
2
|
+
load: () => Promise<unknown>;
|
|
3
|
+
onError?: (error: unknown) => void;
|
|
4
|
+
root?: Element;
|
|
5
|
+
}
|
|
6
|
+
export declare function isInteractionReplay(event: Event): boolean;
|
|
7
|
+
export declare function installInteractionHydration(options: InteractionHydrationOptions): () => void;
|
|
8
|
+
export declare function wakeInteractionHydration(root?: Element): Promise<void>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const INTERACTION_ROOT_SELECTOR = '[data-webui-interaction]';
|
|
2
|
+
const WAKE_EVENTS = [
|
|
3
|
+
'pointerdown',
|
|
4
|
+
'focusin',
|
|
5
|
+
'keydown',
|
|
6
|
+
];
|
|
7
|
+
const installedBoundaries = new WeakMap();
|
|
8
|
+
let compilerRoot;
|
|
9
|
+
const interactionReplays = new WeakMap();
|
|
10
|
+
export function isInteractionReplay(event) {
|
|
11
|
+
return interactionReplays.has(event);
|
|
12
|
+
}
|
|
13
|
+
export function installInteractionHydration(options) {
|
|
14
|
+
const root = options.root ?? compilerMarkedRoot();
|
|
15
|
+
const existing = installedBoundaries.get(root);
|
|
16
|
+
if (existing)
|
|
17
|
+
return existing.dispose;
|
|
18
|
+
let loading;
|
|
19
|
+
let listening = true;
|
|
20
|
+
let disposed = false;
|
|
21
|
+
const disposal = {};
|
|
22
|
+
const dispose = createDisposer(disposal);
|
|
23
|
+
const remove = () => {
|
|
24
|
+
if (!listening)
|
|
25
|
+
return;
|
|
26
|
+
listening = false;
|
|
27
|
+
for (let i = 0; i < WAKE_EVENTS.length; i++) {
|
|
28
|
+
root.removeEventListener(WAKE_EVENTS[i], wake, true);
|
|
29
|
+
}
|
|
30
|
+
root.removeEventListener('click', replay, true);
|
|
31
|
+
installedBoundaries.delete(root);
|
|
32
|
+
disposal.callback = undefined;
|
|
33
|
+
};
|
|
34
|
+
disposal.callback = () => {
|
|
35
|
+
disposed = true;
|
|
36
|
+
remove();
|
|
37
|
+
};
|
|
38
|
+
const ensureLoaded = () => {
|
|
39
|
+
loading ??= Promise.resolve().then(options.load).then(() => {
|
|
40
|
+
remove();
|
|
41
|
+
}, (error) => {
|
|
42
|
+
remove();
|
|
43
|
+
reportFailure(error, options.onError);
|
|
44
|
+
throw error;
|
|
45
|
+
});
|
|
46
|
+
return loading;
|
|
47
|
+
};
|
|
48
|
+
const wake = () => {
|
|
49
|
+
void ensureLoaded().catch(() => { });
|
|
50
|
+
};
|
|
51
|
+
const replay = (event) => {
|
|
52
|
+
if (!listening || hasTraversed(event, root))
|
|
53
|
+
return;
|
|
54
|
+
const target = event.composedPath()[0] ?? event.target;
|
|
55
|
+
const replayEvent = target ? cloneClick(event, target, root) : null;
|
|
56
|
+
if (!replayEvent) {
|
|
57
|
+
wake();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
event.stopImmediatePropagation();
|
|
62
|
+
const dispatchReplay = () => {
|
|
63
|
+
if (!disposed)
|
|
64
|
+
target.dispatchEvent(replayEvent);
|
|
65
|
+
};
|
|
66
|
+
void ensureLoaded().then(dispatchReplay, dispatchReplay);
|
|
67
|
+
};
|
|
68
|
+
installedBoundaries.set(root, { dispose, wake: ensureLoaded });
|
|
69
|
+
for (let i = 0; i < WAKE_EVENTS.length; i++) {
|
|
70
|
+
root.addEventListener(WAKE_EVENTS[i], wake, true);
|
|
71
|
+
}
|
|
72
|
+
root.addEventListener('click', replay, true);
|
|
73
|
+
return dispose;
|
|
74
|
+
}
|
|
75
|
+
function createDisposer(slot) {
|
|
76
|
+
return () => slot.callback?.();
|
|
77
|
+
}
|
|
78
|
+
export function wakeInteractionHydration(root) {
|
|
79
|
+
const target = root ?? compilerMarkedRoot();
|
|
80
|
+
const boundary = installedBoundaries.get(target);
|
|
81
|
+
if (!boundary) {
|
|
82
|
+
throw new Error('[WebUI] interaction hydration boundary is not installed.');
|
|
83
|
+
}
|
|
84
|
+
return boundary.wake();
|
|
85
|
+
}
|
|
86
|
+
function compilerMarkedRoot() {
|
|
87
|
+
const cached = compilerRoot?.deref();
|
|
88
|
+
if (cached && cached.isConnected !== false)
|
|
89
|
+
return cached;
|
|
90
|
+
const roots = compilerMarkedRoots();
|
|
91
|
+
if (roots.length !== 1) {
|
|
92
|
+
throw new Error(`[WebUI] interaction hydration requires exactly one compiler-marked root; found ${roots.length}.`);
|
|
93
|
+
}
|
|
94
|
+
const root = roots[0];
|
|
95
|
+
root.removeAttribute('data-webui-interaction');
|
|
96
|
+
compilerRoot = new WeakRef(root);
|
|
97
|
+
return root;
|
|
98
|
+
}
|
|
99
|
+
function compilerMarkedRoots() {
|
|
100
|
+
const matches = [];
|
|
101
|
+
const scopes = [document];
|
|
102
|
+
for (let scopeIndex = 0; scopeIndex < scopes.length; scopeIndex++) {
|
|
103
|
+
const scope = scopes[scopeIndex];
|
|
104
|
+
const marked = scope.querySelectorAll(INTERACTION_ROOT_SELECTOR);
|
|
105
|
+
for (let i = 0; i < marked.length; i++)
|
|
106
|
+
matches.push(marked[i]);
|
|
107
|
+
const elements = scope.querySelectorAll('*');
|
|
108
|
+
for (let i = 0; i < elements.length; i++) {
|
|
109
|
+
const shadowRoot = elements[i].shadowRoot;
|
|
110
|
+
if (shadowRoot)
|
|
111
|
+
scopes.push(shadowRoot);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return matches;
|
|
115
|
+
}
|
|
116
|
+
function cloneClick(event, target, boundary) {
|
|
117
|
+
const ownerDocument = 'ownerDocument' in target
|
|
118
|
+
? target
|
|
119
|
+
.ownerDocument
|
|
120
|
+
: undefined;
|
|
121
|
+
const view = (ownerDocument?.defaultView);
|
|
122
|
+
const MouseEventConstructor = view?.MouseEvent
|
|
123
|
+
?? (typeof MouseEvent === 'function' ? MouseEvent : undefined);
|
|
124
|
+
if (!MouseEventConstructor
|
|
125
|
+
|| !(event instanceof MouseEventConstructor)
|
|
126
|
+
|| !event.cancelable
|
|
127
|
+
|| event.defaultPrevented
|
|
128
|
+
|| event.button !== 0
|
|
129
|
+
|| event.altKey
|
|
130
|
+
|| event.ctrlKey
|
|
131
|
+
|| event.metaKey
|
|
132
|
+
|| event.shiftKey) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const PointerEventConstructor = view?.PointerEvent
|
|
136
|
+
?? (typeof PointerEvent === 'function' ? PointerEvent : undefined);
|
|
137
|
+
const replayEvent = PointerEventConstructor
|
|
138
|
+
&& event instanceof PointerEventConstructor
|
|
139
|
+
? new PointerEventConstructor('click', event)
|
|
140
|
+
: new MouseEventConstructor('click', event);
|
|
141
|
+
interactionReplays.set(replayEvent, {
|
|
142
|
+
boundary,
|
|
143
|
+
previous: interactionReplays.get(event),
|
|
144
|
+
});
|
|
145
|
+
return replayEvent;
|
|
146
|
+
}
|
|
147
|
+
function hasTraversed(event, boundary) {
|
|
148
|
+
let trail = interactionReplays.get(event);
|
|
149
|
+
while (trail) {
|
|
150
|
+
if (trail.boundary === boundary)
|
|
151
|
+
return true;
|
|
152
|
+
trail = trail.previous;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
function reportFailure(error, onError) {
|
|
157
|
+
if (onError) {
|
|
158
|
+
try {
|
|
159
|
+
onError(error);
|
|
160
|
+
}
|
|
161
|
+
catch (onErrorFailure) {
|
|
162
|
+
console.error('[WebUI] interaction hydration error handler failed:', onErrorFailure);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
console.error(`[WebUI] interaction hydration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
167
|
+
}
|
package/dist/static-host.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import { TemplateElement } from './template-element.js';
|
|
1
|
+
import { consumePendingParentState, TemplateElement, } from './template-element.js';
|
|
2
|
+
import { hasComponentStyleWork } from './element/styles.js';
|
|
2
3
|
import { getTemplateRegistry } from './template.js';
|
|
3
4
|
import { templateNeedsStaticHost } from './template-roots.js';
|
|
5
|
+
import { ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
|
|
4
6
|
import { TEMPLATES_REGISTERED_EVENT, templateRegistrationDetail, } from './template-events.js';
|
|
5
7
|
let runtimeInstalled = false;
|
|
8
|
+
function applyPendingNoopHostState(host) {
|
|
9
|
+
const pending = consumePendingParentState(host);
|
|
10
|
+
if (!pending)
|
|
11
|
+
return;
|
|
12
|
+
const target = host;
|
|
13
|
+
const keys = Object.keys(pending.values);
|
|
14
|
+
for (let i = 0; i < keys.length; i++) {
|
|
15
|
+
const key = keys[i];
|
|
16
|
+
if (!Object.hasOwn(target, key))
|
|
17
|
+
target[key] = pending.values[key];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
6
20
|
function defineTemplateHost(tag, meta) {
|
|
7
21
|
const w = window;
|
|
8
22
|
if (!w.__webui)
|
|
@@ -11,6 +25,22 @@ function defineTemplateHost(tag, meta) {
|
|
|
11
25
|
w.__webui.templates = {};
|
|
12
26
|
if (!w.__webui.templates[tag])
|
|
13
27
|
w.__webui.templates[tag] = meta;
|
|
28
|
+
if (templateIsNoopHost(meta) && !hasComponentStyleWork(tag)) {
|
|
29
|
+
customElements.define(tag, class extends HTMLElement {
|
|
30
|
+
connectedCallback() {
|
|
31
|
+
if (isStreamingHydrationMode() &&
|
|
32
|
+
this.hasAttribute(STREAMED_HOST_ATTR)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
applyPendingNoopHostState(this);
|
|
36
|
+
}
|
|
37
|
+
[STREAMING_BOUNDARY_ACTIVATE]() {
|
|
38
|
+
applyPendingNoopHostState(this);
|
|
39
|
+
return ACTIVATION_STATIC_HOST_OPT_OUT;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
14
44
|
class StaticTemplateHost extends TemplateElement {
|
|
15
45
|
$afterExternalStateWrite(applied) {
|
|
16
46
|
if (applied)
|
|
@@ -28,6 +58,18 @@ function defineTemplateHost(tag, meta) {
|
|
|
28
58
|
}
|
|
29
59
|
StaticTemplateHost.define(tag);
|
|
30
60
|
}
|
|
61
|
+
function templateIsNoopHost(meta) {
|
|
62
|
+
return meta.h.length === 0
|
|
63
|
+
&& meta.tx === undefined
|
|
64
|
+
&& meta.a === undefined
|
|
65
|
+
&& meta.c === undefined
|
|
66
|
+
&& meta.r === undefined
|
|
67
|
+
&& meta.eg === undefined
|
|
68
|
+
&& meta.b === undefined
|
|
69
|
+
&& meta.re === undefined
|
|
70
|
+
&& meta.tr === undefined
|
|
71
|
+
&& meta.sd === undefined;
|
|
72
|
+
}
|
|
31
73
|
function defineMissingTemplateHost(tag, meta) {
|
|
32
74
|
if (!templateNeedsStaticHost(meta) ||
|
|
33
75
|
window.__webui?.templateHostExclusions?.has(tag) ||
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { PendingBoundaryUpdates } from './streaming-deferred.js';
|
|
2
|
-
export declare function activateRootsBetween(startMarker: Comment, endMarker: Comment, state: Record<string, unknown> | undefined, updates?: PendingBoundaryUpdates): void;
|
|
1
|
+
import type { PendingBoundaryUpdates, SpanBypass } from './streaming-deferred.js';
|
|
2
|
+
export declare function activateRootsBetween(startMarker: Comment, endMarker: Comment, state: Record<string, unknown> | undefined, updates?: PendingBoundaryUpdates, bypass?: SpanBypass): void;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { abandonDeferredRange, } from './streaming-cleanup.js';
|
|
2
2
|
import { activateDeferredTree, } from './streaming-deferred.js';
|
|
3
|
-
export function activateRootsBetween(startMarker, endMarker, state, updates) {
|
|
3
|
+
export function activateRootsBetween(startMarker, endMarker, state, updates, bypass) {
|
|
4
4
|
const root = startMarker.parentNode;
|
|
5
5
|
if (!root) {
|
|
6
6
|
throw new Error('boundary start marker was detached before activation');
|
|
7
7
|
}
|
|
8
|
-
const failure = activateDeferredTree(startMarker.nextSibling, root, endMarker, state, updates
|
|
8
|
+
const failure = activateDeferredTree(startMarker.nextSibling, root, endMarker, state, updates || bypass
|
|
9
|
+
? { updates, bypass, countRetention: updates !== undefined }
|
|
10
|
+
: undefined);
|
|
9
11
|
if (!failure)
|
|
10
12
|
return;
|
|
11
13
|
abandonDeferredRange(startMarker, endMarker);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { BoundaryBootstrap } from './streaming-protocol.js';
|
|
2
|
-
export declare function applyBoundaryBootstrap(bootstrap: BoundaryBootstrap): void;
|
|
1
|
+
import type { BoundaryBootstrap, SpanCompletionPayload } from './streaming-protocol.js';
|
|
2
|
+
export declare function applyBoundaryBootstrap(bootstrap: BoundaryBootstrap | SpanCompletionPayload): void;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { registerTemplateData } from './template.js';
|
|
2
|
+
import { registerComponentStyles } from './element/styles.js';
|
|
2
3
|
export function applyBoundaryBootstrap(bootstrap) {
|
|
4
|
+
registerComponentStyles(bootstrap.componentStyles);
|
|
3
5
|
if (bootstrap.templates)
|
|
4
6
|
registerTemplateData(bootstrap.templates);
|
|
5
7
|
const w = window;
|
|
@@ -8,7 +10,11 @@ export function applyBoundaryBootstrap(bootstrap) {
|
|
|
8
10
|
const keys = Object.keys(bootstrap);
|
|
9
11
|
for (let i = 0; i < keys.length; i++) {
|
|
10
12
|
const key = keys[i];
|
|
11
|
-
if (key === 'templates' ||
|
|
13
|
+
if (key === 'templates' ||
|
|
14
|
+
key === 'state' ||
|
|
15
|
+
key === 'componentStyles' ||
|
|
16
|
+
key === 'declarationId' ||
|
|
17
|
+
key === 'enclosingSpanInstanceId')
|
|
12
18
|
continue;
|
|
13
19
|
if (key === 'inventory') {
|
|
14
20
|
w.__webui.inventory = mergeInventory(w.__webui.inventory, bootstrap.inventory);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function abandonDeferredElement(el: Element): void;
|
|
2
|
+
export declare function removeStreamingAttributes(el: Element): void;
|
|
2
3
|
export declare function abandonDeferredDescendants(root: Element): void;
|
|
3
4
|
export declare function abandonDeferredRange(startMarker: Comment, endMarker: Node): void;
|
|
4
5
|
export declare function abandonDeferredDocumentRoots(): void;
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { firstNodeWithin, MAX_MARKER_SCAN_NODES, nextWithinRoot, safeRemoveAttribute, streamingErrorMessage, } from './streaming-dom.js';
|
|
1
|
+
import { BOUNDARY_SCRIPT_ATTR, BOUNDARY_START_PREFIX, firstNodeWithin, isRangeEndMarker, MAX_MARKER_SCAN_NODES, nextAfterSubtreeWithin, nextWithinRoot, safeRemoveAttribute, safeRemove, SPAN_START_PREFIX, STREAMING_ENCLOSING_SPAN_ATTR, STREAMING_SPAN_HOST_ATTR, streamingErrorMessage, } from './streaming-dom.js';
|
|
2
2
|
import { STREAMED_HOST_ATTR } from './streaming-mode.js';
|
|
3
3
|
const STREAMING_BOUNDARY_ABANDON = Symbol.for('microsoft.webui.boundaryAbandon');
|
|
4
|
+
const STREAMING_ROOT_ATTRS = [
|
|
5
|
+
STREAMED_HOST_ATTR,
|
|
6
|
+
STREAMING_SPAN_HOST_ATTR,
|
|
7
|
+
STREAMING_ENCLOSING_SPAN_ATTR,
|
|
8
|
+
];
|
|
4
9
|
export function abandonDeferredElement(el) {
|
|
10
|
+
const marked = hasStreamingAttribute(el);
|
|
5
11
|
try {
|
|
6
|
-
if (!
|
|
12
|
+
if (!marked)
|
|
7
13
|
return;
|
|
8
14
|
}
|
|
9
15
|
catch {
|
|
10
|
-
|
|
16
|
+
removeStreamingAttributes(el);
|
|
11
17
|
return;
|
|
12
18
|
}
|
|
13
19
|
try {
|
|
@@ -19,7 +25,19 @@ export function abandonDeferredElement(el) {
|
|
|
19
25
|
console.error(`[WebUI] streaming: abandon failed for <${el.tagName?.toLowerCase?.() ?? '?'}>: ${streamingErrorMessage(error)}`);
|
|
20
26
|
}
|
|
21
27
|
finally {
|
|
22
|
-
|
|
28
|
+
removeStreamingAttributes(el);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function hasStreamingAttribute(el) {
|
|
32
|
+
for (let i = 0; i < STREAMING_ROOT_ATTRS.length; i++) {
|
|
33
|
+
if (el.hasAttribute(STREAMING_ROOT_ATTRS[i]))
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
export function removeStreamingAttributes(el) {
|
|
39
|
+
for (let i = 0; i < STREAMING_ROOT_ATTRS.length; i++) {
|
|
40
|
+
safeRemoveAttribute(el, STREAMING_ROOT_ATTRS[i]);
|
|
23
41
|
}
|
|
24
42
|
}
|
|
25
43
|
export function abandonDeferredDescendants(root) {
|
|
@@ -47,20 +65,54 @@ export function abandonDeferredDocumentRoots() {
|
|
|
47
65
|
typeof document.getElementsByTagName !== 'function') {
|
|
48
66
|
return;
|
|
49
67
|
}
|
|
68
|
+
const documentRoot = document.documentElement;
|
|
69
|
+
if (documentRoot) {
|
|
70
|
+
abandonStreamingNodes(documentRoot.firstChild, documentRoot);
|
|
71
|
+
abandonDeferredElement(documentRoot);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
50
74
|
const elements = document.getElementsByTagName('*');
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
const limit = elements.length < MAX_MARKER_SCAN_NODES
|
|
76
|
+
? elements.length
|
|
77
|
+
: MAX_MARKER_SCAN_NODES;
|
|
78
|
+
for (let i = 0; i < limit; i++) {
|
|
53
79
|
const el = elements[i];
|
|
54
|
-
visited++;
|
|
55
80
|
abandonDeferredElement(el);
|
|
56
81
|
const shadowRoot = el.shadowRoot;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
if (shadowRoot)
|
|
83
|
+
abandonStreamingNodes(shadowRoot.firstChild, shadowRoot);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function abandonStreamingNodes(first, root) {
|
|
87
|
+
let node = first;
|
|
88
|
+
let visited = 0;
|
|
89
|
+
while (node && visited < MAX_MARKER_SCAN_NODES) {
|
|
90
|
+
visited++;
|
|
91
|
+
if (node.nodeType === 8) {
|
|
92
|
+
const next = nextWithinRoot(node, root);
|
|
93
|
+
if (isStreamingMarker(node.data))
|
|
94
|
+
safeRemove(node);
|
|
95
|
+
node = next;
|
|
96
|
+
continue;
|
|
64
97
|
}
|
|
98
|
+
if (node.nodeType === 1) {
|
|
99
|
+
const el = node;
|
|
100
|
+
const scaffold = el.tagName === 'WEBUI-HYDRATE' ||
|
|
101
|
+
el.hasAttribute(BOUNDARY_SCRIPT_ATTR);
|
|
102
|
+
const next = scaffold
|
|
103
|
+
? nextAfterSubtreeWithin(node, root)
|
|
104
|
+
: nextWithinRoot(node, root);
|
|
105
|
+
abandonDeferredElement(el);
|
|
106
|
+
if (scaffold)
|
|
107
|
+
safeRemove(el);
|
|
108
|
+
node = next;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
node = nextWithinRoot(node, root);
|
|
65
112
|
}
|
|
66
113
|
}
|
|
114
|
+
function isStreamingMarker(data) {
|
|
115
|
+
return data.startsWith(BOUNDARY_START_PREFIX) ||
|
|
116
|
+
data.startsWith(SPAN_START_PREFIX) ||
|
|
117
|
+
isRangeEndMarker(data);
|
|
118
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { elementHasPendingStateForTests, pendingTagWaiterCountForTests, pendingUndefinedRootCountForTests } from './streaming-deferred.js';
|
|
1
|
+
import { elementHasPendingStateForTests, pendingBarrierRootCountForTests, pendingTagWaiterCountForTests, pendingUndefinedRootCountForTests } from './streaming-deferred.js';
|
|
2
|
+
import { openSpanCountForTests } from './streaming-spans.js';
|
|
2
3
|
export declare function enqueueStreamingSentinel(sentinel: Element): void;
|
|
3
4
|
export declare function installStreamingTruncationGuard(): void;
|
|
4
5
|
export declare function resetStreamingCoordinatorStateForTests(): void;
|
|
5
6
|
export declare function isStreamingHaltedForTests(): boolean;
|
|
6
7
|
export declare function streamingRetentionStateForTests(): readonly [number, number];
|
|
7
|
-
export { elementHasPendingStateForTests, pendingTagWaiterCountForTests, pendingUndefinedRootCountForTests, };
|
|
8
|
+
export { elementHasPendingStateForTests, openSpanCountForTests, pendingBarrierRootCountForTests, pendingTagWaiterCountForTests, pendingUndefinedRootCountForTests, };
|