@microsoft/webui-framework 0.0.27 → 0.0.29
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 +26 -2
- package/dist/element/style-catalog.d.ts +33 -0
- package/dist/element/style-catalog.js +154 -0
- package/dist/element/styles.d.ts +2 -29
- package/dist/element/styles.js +4 -157
- package/dist/lifecycle.js +4 -0
- package/dist/static-host.js +2 -1
- package/dist/streaming-bootstrap.js +4 -2
- package/dist/streaming-coordinator.js +12 -7
- package/dist/streaming-deferred.js +44 -15
- package/dist/streaming-install.js +3 -2
- package/dist/streaming-mode.d.ts +2 -1
- package/dist/streaming-mode.js +4 -1
- package/dist/streaming-protocol.d.ts +4 -2
- package/dist/streaming-protocol.js +2 -6
- package/dist/streaming-record-state.d.ts +6 -0
- package/dist/streaming-record-state.js +54 -0
- package/dist/streaming-template-hosts.d.ts +5 -0
- package/dist/streaming-template-hosts.js +39 -0
- package/dist/template-content.d.ts +1 -0
- package/dist/template-content.js +6 -2
- package/dist/template-element.js +43 -41
- package/dist/template-registry.d.ts +29 -0
- package/dist/template-registry.js +228 -0
- package/dist/template.d.ts +1 -25
- package/dist/template.js +4 -206
- package/dist/trusted-types-policy.d.ts +4 -0
- package/dist/trusted-types-policy.js +62 -0
- package/package.json +8 -5
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { markLateActivationPending, settleLateActivation, } from './lifecycle.js';
|
|
2
2
|
import { abandonDeferredDescendants, abandonDeferredElement, removeStreamingAttributes, } from './streaming-cleanup.js';
|
|
3
3
|
import { firstNodeWithin, MAX_ELEMENTS_PER_BOUNDARY, MAX_MARKER_SCAN_NODES, nextAfterSubtreeWithin, nextWithinRoot, STREAMING_ENCLOSING_SPAN_ATTR, streamingErrorMessage, } from './streaming-dom.js';
|
|
4
|
-
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT,
|
|
4
|
+
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, registerStreamingRootResume, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
|
|
5
5
|
import { applyStateUpdate } from './streaming-state.js';
|
|
6
|
+
import { templateNeedsStaticHost } from './template-roots.js';
|
|
7
|
+
import { invalidateTemplateHostRuntime, loadTemplateHostRuntime, resetTemplateHostRuntimeForTests, } from './streaming-template-hosts.js';
|
|
6
8
|
export const ELEMENT_IGNORED = 10;
|
|
7
9
|
export const ELEMENT_DEFERRED = 11;
|
|
8
10
|
export const ELEMENT_LIMIT_FAILURE = 12;
|
|
@@ -20,9 +22,11 @@ const pendingTagWaiters = new Map();
|
|
|
20
22
|
const pendingBarrierRoots = new Set();
|
|
21
23
|
let pendingUndefinedRoots = 0;
|
|
22
24
|
let activationGeneration = 0;
|
|
25
|
+
let templateHostRuntimeRequested = false;
|
|
23
26
|
let failureHandler = null;
|
|
24
27
|
let invalidActivationOutcome;
|
|
25
|
-
|
|
28
|
+
let pendingRootRecords;
|
|
29
|
+
registerStreamingRootResume(resumeRetainedElement);
|
|
26
30
|
export function configureStreamingFailureHandler(handler) {
|
|
27
31
|
failureHandler = handler;
|
|
28
32
|
}
|
|
@@ -89,23 +93,45 @@ function activateMarkedElement(el, state, updates, bypass) {
|
|
|
89
93
|
customElements
|
|
90
94
|
.whenDefined(tag)
|
|
91
95
|
.then(() => onTagDefined(tag, generation));
|
|
96
|
+
requestTemplateHostRuntime(tag, generation);
|
|
92
97
|
}
|
|
93
98
|
if (!waiter.roots.has(el)) {
|
|
94
99
|
waiter.roots.add(el);
|
|
95
100
|
pendingUndefinedRoots++;
|
|
96
101
|
}
|
|
97
|
-
el[PENDING_ROOT_CONNECTED] = resumePendingRoot;
|
|
98
102
|
return ELEMENT_DEFERRED;
|
|
99
103
|
}
|
|
104
|
+
function resumeRetainedElement(el) {
|
|
105
|
+
if (!hasPendingRecord(el))
|
|
106
|
+
return false;
|
|
107
|
+
if (pendingBarrierRoots.has(el))
|
|
108
|
+
resumeBarrierRoot.call(el);
|
|
109
|
+
else
|
|
110
|
+
resumePendingRoot.call(el);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function requestTemplateHostRuntime(tag, generation) {
|
|
114
|
+
if (templateHostRuntimeRequested)
|
|
115
|
+
return;
|
|
116
|
+
const meta = window.__webui?.templates?.[tag];
|
|
117
|
+
if (!meta || !templateNeedsStaticHost(meta))
|
|
118
|
+
return;
|
|
119
|
+
if (window.__webui?.templateHostExclusions?.has(tag))
|
|
120
|
+
return;
|
|
121
|
+
templateHostRuntimeRequested = true;
|
|
122
|
+
void loadTemplateHostRuntime()?.catch((error) => {
|
|
123
|
+
if (generation === activationGeneration) {
|
|
124
|
+
fail(`failed to load template-host runtime: ${streamingErrorMessage(error)}`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
100
128
|
function deferBehindBarrier(el, state, updates, bypass) {
|
|
101
129
|
stashPendingRecord(el, state, updates, bypass);
|
|
102
130
|
pendingBarrierRoots.add(el);
|
|
103
|
-
el[PENDING_ROOT_CONNECTED] = resumeBarrierRoot;
|
|
104
131
|
}
|
|
105
132
|
function activatePendingBarrierRoot(el) {
|
|
106
133
|
if (!pendingBarrierRoots.delete(el))
|
|
107
134
|
return ELEMENT_IGNORED;
|
|
108
|
-
delete el[PENDING_ROOT_CONNECTED];
|
|
109
135
|
const record = takePendingRecord(el);
|
|
110
136
|
const updates = releaseUpdates(record);
|
|
111
137
|
try {
|
|
@@ -180,7 +206,6 @@ function resumePendingRoot() {
|
|
|
180
206
|
if (!waiter ||
|
|
181
207
|
waiter.generation !== activationGeneration ||
|
|
182
208
|
!waiter.roots.has(this)) {
|
|
183
|
-
delete this[PENDING_ROOT_CONNECTED];
|
|
184
209
|
return;
|
|
185
210
|
}
|
|
186
211
|
activatePendingRoot(tag, waiter, this);
|
|
@@ -189,7 +214,6 @@ function activatePendingRoot(tag, waiter, el) {
|
|
|
189
214
|
if (!waiter.roots.delete(el))
|
|
190
215
|
return;
|
|
191
216
|
pendingUndefinedRoots--;
|
|
192
|
-
delete el[PENDING_ROOT_CONNECTED];
|
|
193
217
|
const record = takePendingRecord(el);
|
|
194
218
|
const updates = releaseUpdates(record);
|
|
195
219
|
const state = record?.state;
|
|
@@ -309,6 +333,8 @@ function abandonDeferredTree(el) {
|
|
|
309
333
|
abandonDeferredElement(el);
|
|
310
334
|
}
|
|
311
335
|
export function abandonPendingWaiters() {
|
|
336
|
+
activationGeneration++;
|
|
337
|
+
invalidateTemplateHostRuntime();
|
|
312
338
|
if (pendingBarrierRoots.size !== 0) {
|
|
313
339
|
for (const el of pendingBarrierRoots)
|
|
314
340
|
clearPendingRoot(el);
|
|
@@ -323,28 +349,30 @@ export function abandonPendingWaiters() {
|
|
|
323
349
|
pendingTagWaiters.clear();
|
|
324
350
|
}
|
|
325
351
|
pendingUndefinedRoots = 0;
|
|
352
|
+
pendingRootRecords = undefined;
|
|
326
353
|
}
|
|
327
354
|
function clearPendingRoot(el) {
|
|
328
355
|
releaseUpdates(takePendingRecord(el));
|
|
329
|
-
delete el[PENDING_ROOT_CONNECTED];
|
|
330
356
|
abandonDeferredTree(el);
|
|
331
357
|
}
|
|
332
358
|
function stashPendingRecord(el, state, updates, bypass) {
|
|
333
|
-
el
|
|
359
|
+
(pendingRootRecords ??= new WeakMap()).set(el, {
|
|
334
360
|
state,
|
|
335
361
|
updates,
|
|
336
362
|
bypass,
|
|
337
|
-
};
|
|
363
|
+
});
|
|
338
364
|
if (updates)
|
|
339
365
|
updates.pendingRoots++;
|
|
340
366
|
}
|
|
341
367
|
function hasPendingRecord(el) {
|
|
342
|
-
return
|
|
368
|
+
return pendingRootRecords?.has(el) ?? false;
|
|
343
369
|
}
|
|
344
370
|
function takePendingRecord(el) {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
371
|
+
const record = pendingRootRecords?.get(el);
|
|
372
|
+
pendingRootRecords?.delete(el);
|
|
373
|
+
if (pendingUndefinedRoots === 0 && pendingBarrierRoots.size === 0) {
|
|
374
|
+
pendingRootRecords = undefined;
|
|
375
|
+
}
|
|
348
376
|
return record;
|
|
349
377
|
}
|
|
350
378
|
function releaseUpdates(record) {
|
|
@@ -384,7 +412,8 @@ function invokeActivationHook(el, state, bypass) {
|
|
|
384
412
|
}
|
|
385
413
|
export function resetDeferredActivationForTests() {
|
|
386
414
|
abandonPendingWaiters();
|
|
387
|
-
|
|
415
|
+
templateHostRuntimeRequested = false;
|
|
416
|
+
resetTemplateHostRuntimeForTests();
|
|
388
417
|
}
|
|
389
418
|
export function pendingTagWaiterCountForTests() {
|
|
390
419
|
return pendingTagWaiters.size;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { beginStreamingGate } from './lifecycle.js';
|
|
2
|
-
import { installTemplateElementRuntime } from './static-host.js';
|
|
3
2
|
import { enqueueStreamingSentinel, installStreamingTruncationGuard, } from './streaming-coordinator.js';
|
|
4
3
|
import { isStreamingHydrationMode } from './streaming-mode.js';
|
|
4
|
+
import { installTemplateDefinitionPreparation } from './template-registry.js';
|
|
5
|
+
import { prepareTemplateDefinitions } from './streaming-template-hosts.js';
|
|
5
6
|
const SENTINEL_TAG = 'webui-hydrate';
|
|
6
7
|
let installed = false;
|
|
7
8
|
class WebUiHydrateSentinel extends HTMLElement {
|
|
@@ -13,12 +14,12 @@ export function installStreamingCoordinator() {
|
|
|
13
14
|
if (installed || !isStreamingHydrationMode())
|
|
14
15
|
return;
|
|
15
16
|
installed = true;
|
|
17
|
+
installTemplateDefinitionPreparation(prepareTemplateDefinitions);
|
|
16
18
|
if (!customElements.get(SENTINEL_TAG)) {
|
|
17
19
|
customElements.define(SENTINEL_TAG, WebUiHydrateSentinel);
|
|
18
20
|
}
|
|
19
21
|
beginStreamingGate();
|
|
20
22
|
installStreamingTruncationGuard();
|
|
21
|
-
installTemplateElementRuntime();
|
|
22
23
|
}
|
|
23
24
|
export function resetStreamingInstallForTests() {
|
|
24
25
|
installed = false;
|
package/dist/streaming-mode.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const STREAMING_BOUNDARY_ACTIVATE: unique symbol;
|
|
2
|
-
export declare
|
|
2
|
+
export declare let resumeStreamingRoot: ((element: Element) => boolean) | undefined;
|
|
3
|
+
export declare function registerStreamingRootResume(resume: typeof resumeStreamingRoot): void;
|
|
3
4
|
export declare const STREAMED_HOST_ATTR = "data-ws";
|
|
4
5
|
export declare const ACTIVATION_ACTIVATED = 1;
|
|
5
6
|
export declare const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
|
package/dist/streaming-mode.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
let cached;
|
|
2
2
|
export const STREAMING_BOUNDARY_ACTIVATE = Symbol.for('microsoft.webui.boundaryActivate');
|
|
3
|
-
export
|
|
3
|
+
export let resumeStreamingRoot;
|
|
4
|
+
export function registerStreamingRootResume(resume) {
|
|
5
|
+
resumeStreamingRoot = resume;
|
|
6
|
+
}
|
|
4
7
|
export const STREAMED_HOST_ATTR = 'data-ws';
|
|
5
8
|
export const ACTIVATION_ACTIVATED = 1;
|
|
6
9
|
export const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { TemplateMeta } from './template.js';
|
|
2
2
|
import type { ComponentStyles } from './element/styles.js';
|
|
3
|
-
export declare const STREAMING_PROTOCOL_VERSION = 2;
|
|
4
3
|
export interface BoundaryBootstrap {
|
|
5
4
|
declarationId: number;
|
|
6
5
|
enclosingSpanInstanceId?: number;
|
|
7
6
|
state?: Record<string, unknown>;
|
|
7
|
+
stateRef?: number;
|
|
8
|
+
stateDelta?: Record<string, unknown>;
|
|
8
9
|
templates?: Record<string, TemplateMeta>;
|
|
9
10
|
inventory?: string;
|
|
10
11
|
nonce?: string;
|
|
@@ -22,6 +23,8 @@ export declare const RECORD_KIND_TERMINAL = 4;
|
|
|
22
23
|
export type BoundaryRecordKind = typeof RECORD_KIND_FINAL_CHECKPOINT | typeof RECORD_KIND_UPDATABLE_CHECKPOINT | typeof RECORD_KIND_STATE_UPDATE | typeof RECORD_KIND_SPAN_COMPLETION | typeof RECORD_KIND_TERMINAL;
|
|
23
24
|
export interface SpanCompletionPayload {
|
|
24
25
|
state?: Record<string, unknown>;
|
|
26
|
+
stateRef?: number;
|
|
27
|
+
stateDelta?: Record<string, unknown>;
|
|
25
28
|
templates?: Record<string, TemplateMeta>;
|
|
26
29
|
inventory?: string;
|
|
27
30
|
nonce?: string;
|
|
@@ -32,7 +35,6 @@ export interface SpanCompletionPayload {
|
|
|
32
35
|
}
|
|
33
36
|
export type BoundaryRecordPayload = BoundaryBootstrap | SpanCompletionPayload | Record<string, unknown>;
|
|
34
37
|
export type BoundaryEnvelope = readonly [
|
|
35
|
-
version: number,
|
|
36
38
|
recordSequence: number,
|
|
37
39
|
kind: BoundaryRecordKind,
|
|
38
40
|
target: number,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const STREAMING_PROTOCOL_VERSION = 2;
|
|
2
1
|
export const RECORD_KIND_FINAL_CHECKPOINT = 0;
|
|
3
2
|
export const RECORD_KIND_UPDATABLE_CHECKPOINT = 1;
|
|
4
3
|
export const RECORD_KIND_STATE_UPDATE = 2;
|
|
@@ -15,11 +14,8 @@ export function parseBoundaryEnvelope(text) {
|
|
|
15
14
|
catch {
|
|
16
15
|
return invalid('boundary payload is not valid JSON');
|
|
17
16
|
}
|
|
18
|
-
if (!Array.isArray(parsed) || parsed.length !==
|
|
19
|
-
return invalid('boundary envelope must be a
|
|
20
|
-
}
|
|
21
|
-
if (parsed[0] !== STREAMING_PROTOCOL_VERSION) {
|
|
22
|
-
return invalid(`unsupported boundary envelope version ${JSON.stringify(parsed[0])}`);
|
|
17
|
+
if (!Array.isArray(parsed) || parsed.length !== 4) {
|
|
18
|
+
return invalid('boundary envelope must be a 4-element [recordSequence, kind, target, payload] array');
|
|
23
19
|
}
|
|
24
20
|
return {
|
|
25
21
|
ok: true,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BoundaryBootstrap, SpanCompletionPayload } from './streaming-protocol.js';
|
|
2
|
+
type RangePayload = BoundaryBootstrap | SpanCompletionPayload;
|
|
3
|
+
type StreamedState = Record<string, unknown> | undefined;
|
|
4
|
+
export declare function resolveRangeState(payload: RangePayload, sequence: number): StreamedState;
|
|
5
|
+
export declare function clearRangeState(): void;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
let lastRangeSequence;
|
|
2
|
+
let lastRangeState;
|
|
3
|
+
export function resolveRangeState(payload, sequence) {
|
|
4
|
+
const reference = payload.stateRef;
|
|
5
|
+
if (reference === undefined) {
|
|
6
|
+
if (payload.stateDelta !== undefined) {
|
|
7
|
+
throw new Error('streaming state delta is missing its stateRef');
|
|
8
|
+
}
|
|
9
|
+
const state = payload.state;
|
|
10
|
+
if (state !== undefined && !isStateObject(state)) {
|
|
11
|
+
throw new Error('streaming range state must be an object');
|
|
12
|
+
}
|
|
13
|
+
lastRangeSequence = sequence;
|
|
14
|
+
lastRangeState = cloneState(state);
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
if (!Number.isSafeInteger(reference) ||
|
|
18
|
+
reference < 0 ||
|
|
19
|
+
reference >= sequence) {
|
|
20
|
+
throw new Error(`invalid streaming state reference ${String(reference)}`);
|
|
21
|
+
}
|
|
22
|
+
if (payload.state !== undefined) {
|
|
23
|
+
throw new Error('referenced streaming state must not include complete state');
|
|
24
|
+
}
|
|
25
|
+
if (lastRangeSequence === undefined || reference !== lastRangeSequence) {
|
|
26
|
+
throw new Error(`streaming state reference ${reference} does not match prior range record ${lastRangeSequence === undefined ? 'none' : lastRangeSequence}`);
|
|
27
|
+
}
|
|
28
|
+
const delta = payload.stateDelta;
|
|
29
|
+
if (delta !== undefined && !isStateObject(delta)) {
|
|
30
|
+
throw new Error('streaming state delta must be an object');
|
|
31
|
+
}
|
|
32
|
+
const state = mergeStateDelta(lastRangeState, delta);
|
|
33
|
+
lastRangeSequence = sequence;
|
|
34
|
+
lastRangeState = state;
|
|
35
|
+
return cloneState(state);
|
|
36
|
+
}
|
|
37
|
+
export function clearRangeState() {
|
|
38
|
+
lastRangeSequence = undefined;
|
|
39
|
+
lastRangeState = undefined;
|
|
40
|
+
}
|
|
41
|
+
function isStateObject(value) {
|
|
42
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function mergeStateDelta(base, delta) {
|
|
45
|
+
if (delta === undefined || Object.keys(delta).length === 0)
|
|
46
|
+
return base;
|
|
47
|
+
if (base === undefined) {
|
|
48
|
+
throw new Error('streaming state delta references missing base state');
|
|
49
|
+
}
|
|
50
|
+
return { ...base, ...delta };
|
|
51
|
+
}
|
|
52
|
+
function cloneState(state) {
|
|
53
|
+
return state === undefined ? undefined : structuredClone(state);
|
|
54
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TemplateMeta } from './template-types.js';
|
|
2
|
+
export declare function loadTemplateHostRuntime(): Promise<void> | undefined;
|
|
3
|
+
export declare function prepareTemplateDefinitions(templates: Record<string, TemplateMeta>, names: readonly string[]): Promise<void> | undefined;
|
|
4
|
+
export declare function invalidateTemplateHostRuntime(): void;
|
|
5
|
+
export declare function resetTemplateHostRuntimeForTests(): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { templateNeedsStaticHost } from './template-roots.js';
|
|
2
|
+
let generation = 0;
|
|
3
|
+
let installed = false;
|
|
4
|
+
let pending;
|
|
5
|
+
export function loadTemplateHostRuntime() {
|
|
6
|
+
if (installed)
|
|
7
|
+
return;
|
|
8
|
+
if (!pending) {
|
|
9
|
+
const current = generation;
|
|
10
|
+
pending = import('./static-host.js').then(({ installTemplateElementRuntime }) => {
|
|
11
|
+
if (current !== generation) {
|
|
12
|
+
throw new Error('[WebUI] Template-host activation was abandoned.');
|
|
13
|
+
}
|
|
14
|
+
installTemplateElementRuntime();
|
|
15
|
+
installed = true;
|
|
16
|
+
pending = undefined;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return pending;
|
|
20
|
+
}
|
|
21
|
+
export function prepareTemplateDefinitions(templates, names) {
|
|
22
|
+
const exclusions = window.__webui?.templateHostExclusions;
|
|
23
|
+
for (let i = 0; i < names.length; i++) {
|
|
24
|
+
const tag = names[i];
|
|
25
|
+
if (templateNeedsStaticHost(templates[tag]) &&
|
|
26
|
+
!exclusions?.has(tag) &&
|
|
27
|
+
!customElements.get(tag)) {
|
|
28
|
+
return loadTemplateHostRuntime();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function invalidateTemplateHostRuntime() {
|
|
33
|
+
generation++;
|
|
34
|
+
pending = undefined;
|
|
35
|
+
}
|
|
36
|
+
export function resetTemplateHostRuntimeForTests() {
|
|
37
|
+
invalidateTemplateHostRuntime();
|
|
38
|
+
installed = false;
|
|
39
|
+
}
|
|
@@ -13,5 +13,6 @@ export interface TemplateStylesheetDescriptor {
|
|
|
13
13
|
readonly type: string;
|
|
14
14
|
}
|
|
15
15
|
export declare function cloneTemplateContent(meta: TemplateBlockMeta): DocumentFragment;
|
|
16
|
+
export declare function getTemplateFragment(meta: TemplateBlockMeta): DocumentFragment;
|
|
16
17
|
export declare function getTemplateStylesheets(meta: TemplateBlockMeta): readonly TemplateStylesheetDescriptor[] | undefined;
|
|
17
18
|
export declare function templateHtmlMayContainLink(html: string): boolean;
|
package/dist/template-content.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { setTemplateContent } from './trusted-types-policy.js';
|
|
1
2
|
const EMPTY_STYLESHEETS = Object.freeze([]);
|
|
2
3
|
const NO_LINK_STYLESHEETS = Object.freeze([]);
|
|
3
4
|
const templateContentCache = new WeakMap();
|
|
4
5
|
export function cloneTemplateContent(meta) {
|
|
5
|
-
return
|
|
6
|
+
return getTemplateFragment(meta).cloneNode(true);
|
|
7
|
+
}
|
|
8
|
+
export function getTemplateFragment(meta) {
|
|
9
|
+
return getTemplateContent(meta).fragment;
|
|
6
10
|
}
|
|
7
11
|
export function getTemplateStylesheets(meta) {
|
|
8
12
|
const stylesheets = getTemplateContent(meta, true).stylesheets;
|
|
@@ -37,7 +41,7 @@ function getTemplateContent(meta, mayContainLink) {
|
|
|
37
41
|
if (cached)
|
|
38
42
|
return cached;
|
|
39
43
|
const template = document.createElement('template');
|
|
40
|
-
template
|
|
44
|
+
setTemplateContent(template, meta);
|
|
41
45
|
const fragment = template.content;
|
|
42
46
|
const stylesheets = (mayContainLink ?? templateHtmlMayContainLink(meta.h))
|
|
43
47
|
? collectStylesheetDescriptors(fragment)
|
package/dist/template-element.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deferTemplateDefinition, getTemplate } from './template.js';
|
|
2
|
-
import { cloneTemplateContent, } from './template-content.js';
|
|
2
|
+
import { cloneTemplateContent, getTemplateFragment, } from './template-content.js';
|
|
3
3
|
import { hydrationStart, hydrationEnd } from './lifecycle.js';
|
|
4
|
-
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode,
|
|
4
|
+
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode, resumeStreamingRoot, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
|
|
5
5
|
import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
|
|
6
6
|
import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, rawMarker, } from './element/markers.js';
|
|
7
7
|
import { claimSsrComponentStyles, installComponentStyles, isComponentStyleMarker, } from './element/styles.js';
|
|
@@ -10,7 +10,6 @@ import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLive
|
|
|
10
10
|
import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
|
|
11
11
|
const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
|
|
12
12
|
const rootTagCache = new WeakMap();
|
|
13
|
-
const templateDOMCache = new WeakMap();
|
|
14
13
|
const tplOrdinalCache = new WeakMap();
|
|
15
14
|
const textMarkerBoundaryCache = new WeakMap();
|
|
16
15
|
const NO_TEXT_MARKER_BOUNDARIES = new Int32Array(0);
|
|
@@ -149,15 +148,6 @@ function childNodesArray(parent) {
|
|
|
149
148
|
result[i] = children[i];
|
|
150
149
|
return result;
|
|
151
150
|
}
|
|
152
|
-
function getTemplateDom(meta) {
|
|
153
|
-
let cached = templateDOMCache.get(meta);
|
|
154
|
-
if (cached)
|
|
155
|
-
return cached;
|
|
156
|
-
const div = document.createElement('div');
|
|
157
|
-
div.innerHTML = meta.h;
|
|
158
|
-
templateDOMCache.set(meta, div);
|
|
159
|
-
return div;
|
|
160
|
-
}
|
|
161
151
|
function installTemplateObservedAttributes(ctor, tagName, meta = getTemplate(tagName)) {
|
|
162
152
|
if (!meta)
|
|
163
153
|
return;
|
|
@@ -295,11 +285,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
295
285
|
this.$hasAncestorBoundaryState = undefined;
|
|
296
286
|
}
|
|
297
287
|
$resumeRetainedRoot() {
|
|
298
|
-
|
|
299
|
-
if (typeof resume !== 'function')
|
|
300
|
-
return false;
|
|
301
|
-
resume.call(this);
|
|
302
|
-
return true;
|
|
288
|
+
return resumeStreamingRoot?.(this) ?? false;
|
|
303
289
|
}
|
|
304
290
|
static define(tagName) {
|
|
305
291
|
const ctor = this;
|
|
@@ -448,7 +434,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
448
434
|
this.$applyPendingParentState(isSSR);
|
|
449
435
|
}
|
|
450
436
|
if (isSSR) {
|
|
451
|
-
this.$root = this.$hydrate(root, meta,
|
|
437
|
+
this.$root = this.$hydrate(root, meta, getTemplateFragment(meta));
|
|
452
438
|
}
|
|
453
439
|
else {
|
|
454
440
|
clientRoot = this.$createStagingRoot(meta);
|
|
@@ -1054,31 +1040,47 @@ export class TemplateElement extends HTMLElement {
|
|
|
1054
1040
|
this.$pendingFlush = false;
|
|
1055
1041
|
return;
|
|
1056
1042
|
}
|
|
1057
|
-
|
|
1058
|
-
this.$
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1043
|
+
try {
|
|
1044
|
+
if (!this.$pathIndex)
|
|
1045
|
+
this.$buildPathIndex();
|
|
1046
|
+
if (!this.$pathIndex)
|
|
1047
|
+
return;
|
|
1048
|
+
while (this.$dirtyPaths && this.$dirtyPaths.size > 0) {
|
|
1049
|
+
const dirty = this.$dirtyPaths;
|
|
1050
|
+
this.$dirtyPaths = null;
|
|
1051
|
+
const paths = dirty.values();
|
|
1052
|
+
try {
|
|
1053
|
+
for (const path of paths) {
|
|
1054
|
+
if (!this.$pathIndex)
|
|
1055
|
+
this.$buildPathIndex();
|
|
1056
|
+
const entry = this.$pathIndex?.get(path);
|
|
1057
|
+
if (entry) {
|
|
1058
|
+
this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats, requireKnownState);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
for (const path of paths)
|
|
1064
|
+
(this.$dirtyPaths ??= new Set()).add(path);
|
|
1065
|
+
throw error;
|
|
1066
|
+
}
|
|
1065
1067
|
if (!this.$pathIndex)
|
|
1066
1068
|
this.$buildPathIndex();
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
this.$updateBindings(
|
|
1069
|
+
if (this.$wildcardBindings) {
|
|
1070
|
+
const wc = this.$wildcardBindings;
|
|
1071
|
+
this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats, requireKnownState);
|
|
1070
1072
|
}
|
|
1073
|
+
if (!this.$pathIndex)
|
|
1074
|
+
this.$buildPathIndex();
|
|
1071
1075
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
this.$
|
|
1076
|
+
}
|
|
1077
|
+
finally {
|
|
1078
|
+
this.$pendingFlush = false;
|
|
1079
|
+
if (this.$dirtyPaths?.size) {
|
|
1080
|
+
this.$pendingFlush = true;
|
|
1081
|
+
queueMicrotask(() => this.$flush());
|
|
1077
1082
|
}
|
|
1078
|
-
if (!this.$pathIndex)
|
|
1079
|
-
this.$buildPathIndex();
|
|
1080
1083
|
}
|
|
1081
|
-
this.$pendingFlush = false;
|
|
1082
1084
|
}
|
|
1083
1085
|
$recordPreReadyWrite(path) {
|
|
1084
1086
|
if (!this.$preReadyWrites)
|
|
@@ -1391,7 +1393,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1391
1393
|
const marker = repMarkers ? repMarkers[i] : null;
|
|
1392
1394
|
const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
|
|
1393
1395
|
const blockMeta = this.$block(blockIndex);
|
|
1394
|
-
const blockTplDom = blockMeta ?
|
|
1396
|
+
const blockTplDom = blockMeta ? getTemplateFragment(blockMeta) : null;
|
|
1395
1397
|
const rootTag = blockMeta
|
|
1396
1398
|
&& blockTplDom?.childNodes.length === 1
|
|
1397
1399
|
&& blockTplDom.children.length === 1
|
|
@@ -1540,7 +1542,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1540
1542
|
}
|
|
1541
1543
|
$hydrateCondContent(condAnchor, blockMeta, scope) {
|
|
1542
1544
|
const rootTag = this.$rootTag(blockMeta);
|
|
1543
|
-
const tplDom =
|
|
1545
|
+
const tplDom = getTemplateFragment(blockMeta);
|
|
1544
1546
|
if (rootTag && tplDom.children.length === 1 && !this.$hasRootStructuralSlot(blockMeta)) {
|
|
1545
1547
|
const el = nextElement(condAnchor);
|
|
1546
1548
|
if (el) {
|
|
@@ -1871,11 +1873,11 @@ export class TemplateElement extends HTMLElement {
|
|
|
1871
1873
|
const range = document.createRange();
|
|
1872
1874
|
range.setStartAfter(b.node);
|
|
1873
1875
|
range.setEndBefore(b.rawEnd);
|
|
1876
|
+
const fragment = val !== '' ? range.createContextualFragment(val) : undefined;
|
|
1874
1877
|
range.deleteContents();
|
|
1875
1878
|
let rawNodes;
|
|
1876
1879
|
const rawOwner = b.rawOwner;
|
|
1877
|
-
if (
|
|
1878
|
-
const fragment = range.createContextualFragment(val);
|
|
1880
|
+
if (fragment) {
|
|
1879
1881
|
if (rawOwner && b.node.parentNode === rawOwner.container) {
|
|
1880
1882
|
rawNodes = childNodesArray(fragment);
|
|
1881
1883
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type { CompiledAttrGroupMeta, CompiledAttrMeta, CompiledAttrPart, CompiledCondition, CompiledConditionFn, CompiledConditionalMeta, CompiledEventArg, CompiledEventArgs, CompiledEventBindingMeta, CompiledEventGroupMeta, SerializedCompiledCondition, TemplateCondition, CompiledTextRunMeta, TemplateBlockMeta, TemplateMeta, TemplateNodeIndex, TemplateSlot, } from './template-types.js';
|
|
2
|
+
import { type ComponentStyles } from './element/style-catalog.js';
|
|
3
|
+
import type { CompiledConditionFn, TemplateMeta } from './template-types.js';
|
|
4
|
+
declare let prepareResources: ((templates: Record<string, TemplateMeta>) => Promise<void> | undefined) | undefined;
|
|
5
|
+
type DefinitionPreparation = (templates: Record<string, TemplateMeta>, names: readonly string[]) => Promise<void> | undefined;
|
|
6
|
+
export declare function installTemplateResourcePreparation(prepare: NonNullable<typeof prepareResources>): void;
|
|
7
|
+
export declare function installTemplateDefinitionPreparation(prepare: DefinitionPreparation | null): void;
|
|
8
|
+
declare global {
|
|
9
|
+
interface WebUIRuntimeGlobal {
|
|
10
|
+
state?: Record<string, unknown>;
|
|
11
|
+
templates?: Record<string, TemplateMeta>;
|
|
12
|
+
templateFns?: Record<string, CompiledConditionFn[]>;
|
|
13
|
+
componentAssetStyles?: Record<string, readonly string[]>;
|
|
14
|
+
templateHostExclusions?: Set<string>;
|
|
15
|
+
componentStyles?: ComponentStyles;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface Window {
|
|
19
|
+
__webui?: WebUIRuntimeGlobal;
|
|
20
|
+
__webuiRegisterComponentStyles?: (value: unknown) => Promise<void> | undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export declare function getTemplate(name: string): TemplateMeta | undefined;
|
|
24
|
+
export declare function getTemplateRegistry(): Record<string, TemplateMeta> | undefined;
|
|
25
|
+
export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>, componentStylesValue?: ComponentStyles | unknown): void;
|
|
26
|
+
export declare function prepareAssetTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
|
|
27
|
+
export declare function deferTemplateDefinition(tagName: string, ctor: Function, define: () => void): void;
|
|
28
|
+
export declare function releaseSSRBootstrapState(): void;
|
|
29
|
+
export declare function releaseNonRoutedSSRBootstrapState(): void;
|