@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 CHANGED
@@ -16,6 +16,10 @@ If you are building WebUI apps in this repo, this is the component model used by
16
16
 
17
17
  ## Install
18
18
 
19
+ Trusted Types support is automatic; no setup call is needed. To enforce it,
20
+ allow the `webui` policy in CSP. See [Trusted Types](https://microsoft.github.io/webui/guide/concepts/hydration#trusted-types)
21
+ for CSP requirements and raw-HTML limits.
22
+
19
23
  In this workspace:
20
24
 
21
25
  ```json
@@ -246,6 +250,20 @@ authored `<boundary>` directives through
246
250
  `@microsoft/webui-framework` entry has no dependency on the coordinator, so
247
251
  normal applications pay no streaming bundle or initialization cost.
248
252
 
253
+ To keep the full application out of `<head>`, put the explicit
254
+ `import '@microsoft/webui-framework/streaming.js'` in a small application-owned
255
+ entry. Register that file with your bundler, preserve its side effect, and
256
+ share framework modules with the application. Identify its output and static
257
+ dependencies through native bundler metadata and your existing asset handoff.
258
+ WebUI does not create entries, inject imports, or require a streaming manifest.
259
+
260
+ Load the coordinator with `type="module" async`; marker-based deferral handles
261
+ application-first delivery without a second framework bundle.
262
+ The application may load later, but early-interactive components still need
263
+ their registration modules. Use `fetchpriority="low"` on deferred authored
264
+ module scripts to exclude them from automatic modulepreload hints. See
265
+ [Separate coordinator and application assets](https://microsoft.github.io/webui/guide/concepts/hydration#separate-coordinator-and-application-assets).
266
+
249
267
  Boundaries may be authored in entries and reusable components, including
250
268
  runtime conditions, outlets, and selected routes. A boundary-bearing subtree
251
269
  reached from a `<for>` body fails the build with `boundary-in-repeat`. A whole
@@ -269,8 +287,8 @@ activation. The coordinator does not publish that state to
269
287
  scaffolding after commit. Updates apply state to retained roots and never insert
270
288
  markup or rerun hydration.
271
289
 
272
- The browser reads version-2
273
- `[2, sequence, kind, target, payload]` records for final checkpoints,
290
+ The browser reads the single unversioned
291
+ `[sequence, kind, target, payload]` contract for final checkpoints,
274
292
  updatable checkpoints, updates, span completions, and terminal. Every commit
275
293
  also emits a `performance.mark()` - `webui:boundary:<id>`,
276
294
  `webui:boundary:<id>:update`, `webui:span:<id>`, or
@@ -278,6 +296,12 @@ also emits a `performance.mark()` - `webui:boundary:<id>`,
278
296
  Set `window.__WEBUI_STREAMING_DEBUG__ = true` only when tooling needs the live
279
297
  `webui:boundary-hydrated` event as well.
280
298
 
299
+ A range record can reuse the exact preceding range state with `stateRef` and
300
+ carry only a top-level `stateDelta`. References are backward-only and resolved
301
+ before activation; missing, stale, forward, or malformed references halt and
302
+ clean up the stream. The reference base is released on terminal, cancellation,
303
+ failure, or coordinator reset.
304
+
281
305
  Set `window.__WEBUI_STREAMING_SLICE_MS__` to a positive millisecond budget to
282
306
  make the coordinator yield between boundaries instead of draining its queue in
283
307
  one pass. That is for pages where an intermediary coalesces the response into a
@@ -0,0 +1,33 @@
1
+ export type ComponentStyleResource = ({
2
+ kind: 'link';
3
+ href: string;
4
+ } | {
5
+ kind: 'style';
6
+ css: string;
7
+ } | {
8
+ kind: 'module';
9
+ specifier: string;
10
+ css: string;
11
+ }) & {
12
+ members?: string[];
13
+ };
14
+ export interface ComponentStyles {
15
+ version: 1;
16
+ strategy: 'link' | 'style' | 'module';
17
+ resources: Record<string, ComponentStyleResource>;
18
+ closures: Record<string, string[]>;
19
+ }
20
+ export interface DocumentCatalog {
21
+ readonly resources: Map<string, ComponentStyleResource>;
22
+ readonly closures: Map<string, readonly string[]>;
23
+ }
24
+ export declare function catalogFor(document: Document): DocumentCatalog;
25
+ export declare function sameComponentStyleResource(current: ComponentStyleResource, next: ComponentStyleResource): boolean;
26
+ export declare function sameComponentStyleClosure(current: readonly string[], next: readonly string[]): boolean;
27
+ export declare function prepareComponentStyles(value: unknown): ComponentStyles | undefined;
28
+ export declare function requireComponentStyles(value: unknown): ComponentStyles;
29
+ export declare function validateComponentStylesRegistration(styles: ComponentStyles | undefined, document?: Document): void;
30
+ export declare function registerPreparedComponentStyles(styles: ComponentStyles, document?: Document): void;
31
+ export declare function registerComponentStyles(value: ComponentStyles | unknown, document?: Document): void;
32
+ export declare function hasRegisteredComponentStyleResource(id: string, document?: Document): boolean;
33
+ export declare function hasComponentStyleWork(rootId: string, document?: Document): boolean;
@@ -0,0 +1,154 @@
1
+ const catalogs = new WeakMap();
2
+ export function catalogFor(document) {
3
+ let catalog = catalogs.get(document);
4
+ if (!catalog) {
5
+ catalog = {
6
+ resources: new Map(),
7
+ closures: new Map(),
8
+ };
9
+ catalogs.set(document, catalog);
10
+ }
11
+ return catalog;
12
+ }
13
+ function isObject(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
15
+ }
16
+ export function sameComponentStyleResource(current, next) {
17
+ if (current === next)
18
+ return true;
19
+ const currentMembers = current.members;
20
+ const nextMembers = next.members;
21
+ if (currentMembers?.length !== nextMembers?.length ||
22
+ currentMembers?.some((member, index) => member !== nextMembers?.[index])) {
23
+ return false;
24
+ }
25
+ switch (current.kind) {
26
+ case 'link':
27
+ return next.kind === 'link' && current.href === next.href;
28
+ case 'style':
29
+ return next.kind === 'style' && current.css === next.css;
30
+ case 'module':
31
+ return next.kind === 'module' &&
32
+ current.specifier === next.specifier &&
33
+ current.css === next.css;
34
+ }
35
+ }
36
+ export function sameComponentStyleClosure(current, next) {
37
+ return current === next ||
38
+ (current.length === next.length && current.every((id, index) => id === next[index]));
39
+ }
40
+ export function prepareComponentStyles(value) {
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (!isObject(value) || value.version !== 1) {
44
+ throw new Error('[WebUI] componentStyles must use version 1.');
45
+ }
46
+ if (value.strategy !== 'link' && value.strategy !== 'style' && value.strategy !== 'module') {
47
+ throw new Error('[WebUI] componentStyles strategy must be "link", "style", or "module".');
48
+ }
49
+ if (!isObject(value.resources) || !isObject(value.closures)) {
50
+ throw new Error('[WebUI] componentStyles resources and closures must be objects.');
51
+ }
52
+ const strategy = value.strategy;
53
+ const resources = {};
54
+ for (const id of Object.keys(value.resources)) {
55
+ const resource = value.resources[id];
56
+ if (!id || !isObject(resource) || resource.kind !== strategy) {
57
+ throw new Error(`[WebUI] Invalid component style resource "${id}".`);
58
+ }
59
+ let members;
60
+ if (resource.members !== undefined) {
61
+ if (!Array.isArray(resource.members) ||
62
+ resource.members.length < 2 ||
63
+ resource.members.some(member => typeof member !== 'string' || !member) ||
64
+ new Set(resource.members).size !== resource.members.length) {
65
+ throw new Error(`[WebUI] Invalid component style resource members "${id}".`);
66
+ }
67
+ members = [...resource.members];
68
+ }
69
+ if (resource.kind === 'link' && typeof resource.href === 'string' && resource.href) {
70
+ resources[id] = members
71
+ ? { kind: 'link', href: resource.href, members }
72
+ : { kind: 'link', href: resource.href };
73
+ }
74
+ else if (resource.kind === 'style' && typeof resource.css === 'string') {
75
+ resources[id] = members
76
+ ? { kind: 'style', css: resource.css, members }
77
+ : { kind: 'style', css: resource.css };
78
+ }
79
+ else if (resource.kind === 'module' &&
80
+ typeof resource.specifier === 'string' && resource.specifier &&
81
+ typeof resource.css === 'string') {
82
+ resources[id] = members
83
+ ? { kind: 'module', specifier: resource.specifier, css: resource.css, members }
84
+ : { kind: 'module', specifier: resource.specifier, css: resource.css };
85
+ }
86
+ else {
87
+ throw new Error(`[WebUI] Invalid component style resource "${id}".`);
88
+ }
89
+ }
90
+ const closures = {};
91
+ for (const root of Object.keys(value.closures)) {
92
+ const closure = value.closures[root];
93
+ if (!root || !Array.isArray(closure)) {
94
+ throw new Error(`[WebUI] Invalid component style closure "${root}".`);
95
+ }
96
+ closures[root] = closure.map((id) => {
97
+ if (typeof id !== 'string' || !id) {
98
+ throw new Error(`[WebUI] Invalid resource ID in component style closure "${root}".`);
99
+ }
100
+ return id;
101
+ });
102
+ }
103
+ return { version: 1, strategy, resources, closures };
104
+ }
105
+ export function requireComponentStyles(value) {
106
+ const styles = prepareComponentStyles(value);
107
+ if (!styles) {
108
+ throw new Error('[WebUI] componentStyles is required.');
109
+ }
110
+ return styles;
111
+ }
112
+ export function validateComponentStylesRegistration(styles, document = globalThis.document) {
113
+ if (!styles)
114
+ return;
115
+ const catalog = catalogFor(document);
116
+ for (const id of Object.keys(styles.resources)) {
117
+ const current = catalog.resources.get(id);
118
+ if (current && !sameComponentStyleResource(current, styles.resources[id])) {
119
+ throw new Error(`[WebUI] Conflicting component style resource "${id}".`);
120
+ }
121
+ }
122
+ for (const root of Object.keys(styles.closures)) {
123
+ const current = catalog.closures.get(root);
124
+ const next = styles.closures[root];
125
+ if (current && !sameComponentStyleClosure(current, next)) {
126
+ throw new Error(`[WebUI] Conflicting component style closure "${root}".`);
127
+ }
128
+ }
129
+ }
130
+ export function registerPreparedComponentStyles(styles, document = globalThis.document) {
131
+ const catalog = catalogFor(document);
132
+ for (const id of Object.keys(styles.resources)) {
133
+ if (catalog.resources.has(id))
134
+ continue;
135
+ const resource = styles.resources[id];
136
+ catalog.resources.set(id, resource);
137
+ }
138
+ for (const root of Object.keys(styles.closures)) {
139
+ if (!catalog.closures.has(root)) {
140
+ catalog.closures.set(root, styles.closures[root]);
141
+ }
142
+ }
143
+ }
144
+ export function registerComponentStyles(value, document = globalThis.document) {
145
+ const styles = requireComponentStyles(value);
146
+ validateComponentStylesRegistration(styles, document);
147
+ registerPreparedComponentStyles(styles, document);
148
+ }
149
+ export function hasRegisteredComponentStyleResource(id, document = globalThis.document) {
150
+ return catalogFor(document).resources.has(id);
151
+ }
152
+ export function hasComponentStyleWork(rootId, document = globalThis.document) {
153
+ return (catalogFor(document).closures.get(rootId)?.length ?? 0) > 0;
154
+ }
@@ -1,39 +1,12 @@
1
- export type ComponentStyleResource = ({
2
- kind: 'link';
3
- href: string;
4
- } | {
5
- kind: 'style';
6
- css: string;
7
- } | {
8
- kind: 'module';
9
- specifier: string;
10
- css: string;
11
- }) & {
12
- members?: string[];
13
- };
14
- export interface ComponentStyles {
15
- version: 1;
16
- strategy: 'link' | 'style' | 'module';
17
- resources: Record<string, ComponentStyleResource>;
18
- closures: Record<string, string[]>;
19
- }
1
+ export { hasComponentStyleWork, hasRegisteredComponentStyleResource, prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, requireComponentStyles, sameComponentStyleClosure, sameComponentStyleResource, validateComponentStylesRegistration, } from './style-catalog.js';
2
+ export type { ComponentStyleResource, ComponentStyles } from './style-catalog.js';
20
3
  type StyleTarget = Document | ShadowRoot;
21
4
  type CssModule = {
22
5
  default: CSSStyleSheet;
23
6
  };
24
7
  type CssModuleLoader = (specifier: string) => Promise<CssModule>;
25
8
  export declare function setCssModuleLoaderForTests(loader?: CssModuleLoader): void;
26
- export declare function sameComponentStyleResource(current: ComponentStyleResource, next: ComponentStyleResource): boolean;
27
- export declare function sameComponentStyleClosure(current: readonly string[], next: readonly string[]): boolean;
28
- export declare function prepareComponentStyles(value: unknown): ComponentStyles | undefined;
29
- export declare function requireComponentStyles(value: unknown): ComponentStyles;
30
- export declare function validateComponentStylesRegistration(styles: ComponentStyles | undefined, document?: Document): void;
31
- export declare function registerPreparedComponentStyles(styles: ComponentStyles, document?: Document): void;
32
- export declare function registerComponentStyles(value: ComponentStyles | unknown, document?: Document): void;
33
- export declare function hasRegisteredComponentStyleResource(id: string, document?: Document): boolean;
34
- export declare function hasComponentStyleWork(rootId: string, document?: Document): boolean;
35
9
  export declare function isComponentStyleMarker(element: Element): boolean;
36
10
  export declare function isComponentStyleResourceMarker(element: Element): boolean;
37
11
  export declare function claimSsrComponentStyles(source: Element, target: StyleTarget): void;
38
12
  export declare function installComponentStyles(rootId: string, target: StyleTarget, host?: HTMLElement): Promise<void> | undefined;
39
- export {};
@@ -1,6 +1,8 @@
1
1
  import { installComponentLinkStyles } from './link-styles.js';
2
+ import { setImportMapContent } from '../trusted-types-policy.js';
3
+ import { catalogFor } from './style-catalog.js';
4
+ export { hasComponentStyleWork, hasRegisteredComponentStyleResource, prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, requireComponentStyles, sameComponentStyleClosure, sameComponentStyleResource, validateComponentStylesRegistration, } from './style-catalog.js';
2
5
  const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
3
- const catalogs = new WeakMap();
4
6
  const installed = new WeakMap();
5
7
  const completedClosures = new WeakMap();
6
8
  const pending = new WeakMap();
@@ -14,26 +16,12 @@ let loadCssModule = defaultCssModuleLoader;
14
16
  export function setCssModuleLoaderForTests(loader) {
15
17
  loadCssModule = loader ?? defaultCssModuleLoader;
16
18
  }
17
- function catalogFor(document) {
18
- let catalog = catalogs.get(document);
19
- if (!catalog) {
20
- catalog = {
21
- resources: new Map(),
22
- closures: new Map(),
23
- };
24
- catalogs.set(document, catalog);
25
- }
26
- return catalog;
27
- }
28
19
  function isDocument(target) {
29
20
  return target.nodeType === 9;
30
21
  }
31
22
  function owningDocument(target) {
32
23
  return isDocument(target) ? target : target.ownerDocument;
33
24
  }
34
- function isObject(value) {
35
- return typeof value === 'object' && value !== null && !Array.isArray(value);
36
- }
37
25
  function styleGlobal(owner) {
38
26
  const configured = owner.defaultView?.__webui;
39
27
  if (configured || owner !== globalThis.document || typeof window === 'undefined') {
@@ -49,145 +37,6 @@ function readNonce(target = document) {
49
37
  const meta = owner.querySelector('meta[name="webui-nonce"]');
50
38
  return meta?.content ?? '';
51
39
  }
52
- export function sameComponentStyleResource(current, next) {
53
- if (current === next)
54
- return true;
55
- const currentMembers = current.members;
56
- const nextMembers = next.members;
57
- if (currentMembers?.length !== nextMembers?.length ||
58
- currentMembers?.some((member, index) => member !== nextMembers?.[index])) {
59
- return false;
60
- }
61
- switch (current.kind) {
62
- case 'link':
63
- return next.kind === 'link' && current.href === next.href;
64
- case 'style':
65
- return next.kind === 'style' && current.css === next.css;
66
- case 'module':
67
- return next.kind === 'module' &&
68
- current.specifier === next.specifier &&
69
- current.css === next.css;
70
- }
71
- }
72
- export function sameComponentStyleClosure(current, next) {
73
- return current === next ||
74
- (current.length === next.length && current.every((id, index) => id === next[index]));
75
- }
76
- export function prepareComponentStyles(value) {
77
- if (value === undefined)
78
- return undefined;
79
- if (!isObject(value) || value.version !== 1) {
80
- throw new Error('[WebUI] componentStyles must use version 1.');
81
- }
82
- if (value.strategy !== 'link' && value.strategy !== 'style' && value.strategy !== 'module') {
83
- throw new Error('[WebUI] componentStyles strategy must be "link", "style", or "module".');
84
- }
85
- if (!isObject(value.resources) || !isObject(value.closures)) {
86
- throw new Error('[WebUI] componentStyles resources and closures must be objects.');
87
- }
88
- const strategy = value.strategy;
89
- const resources = {};
90
- for (const id of Object.keys(value.resources)) {
91
- const resource = value.resources[id];
92
- if (!id || !isObject(resource) || resource.kind !== strategy) {
93
- throw new Error(`[WebUI] Invalid component style resource "${id}".`);
94
- }
95
- let members;
96
- if (resource.members !== undefined) {
97
- if (!Array.isArray(resource.members) ||
98
- resource.members.length < 2 ||
99
- resource.members.some(member => typeof member !== 'string' || !member) ||
100
- new Set(resource.members).size !== resource.members.length) {
101
- throw new Error(`[WebUI] Invalid component style resource members "${id}".`);
102
- }
103
- members = [...resource.members];
104
- }
105
- if (resource.kind === 'link' && typeof resource.href === 'string' && resource.href) {
106
- resources[id] = members
107
- ? { kind: 'link', href: resource.href, members }
108
- : { kind: 'link', href: resource.href };
109
- }
110
- else if (resource.kind === 'style' && typeof resource.css === 'string') {
111
- resources[id] = members
112
- ? { kind: 'style', css: resource.css, members }
113
- : { kind: 'style', css: resource.css };
114
- }
115
- else if (resource.kind === 'module' &&
116
- typeof resource.specifier === 'string' && resource.specifier &&
117
- typeof resource.css === 'string') {
118
- resources[id] = members
119
- ? { kind: 'module', specifier: resource.specifier, css: resource.css, members }
120
- : { kind: 'module', specifier: resource.specifier, css: resource.css };
121
- }
122
- else {
123
- throw new Error(`[WebUI] Invalid component style resource "${id}".`);
124
- }
125
- }
126
- const closures = {};
127
- for (const root of Object.keys(value.closures)) {
128
- const closure = value.closures[root];
129
- if (!root || !Array.isArray(closure)) {
130
- throw new Error(`[WebUI] Invalid component style closure "${root}".`);
131
- }
132
- closures[root] = closure.map((id) => {
133
- if (typeof id !== 'string' || !id) {
134
- throw new Error(`[WebUI] Invalid resource ID in component style closure "${root}".`);
135
- }
136
- return id;
137
- });
138
- }
139
- return { version: 1, strategy, resources, closures };
140
- }
141
- export function requireComponentStyles(value) {
142
- const styles = prepareComponentStyles(value);
143
- if (!styles) {
144
- throw new Error('[WebUI] componentStyles is required.');
145
- }
146
- return styles;
147
- }
148
- export function validateComponentStylesRegistration(styles, document = globalThis.document) {
149
- if (!styles)
150
- return;
151
- const catalog = catalogFor(document);
152
- for (const id of Object.keys(styles.resources)) {
153
- const current = catalog.resources.get(id);
154
- if (current && !sameComponentStyleResource(current, styles.resources[id])) {
155
- throw new Error(`[WebUI] Conflicting component style resource "${id}".`);
156
- }
157
- }
158
- for (const root of Object.keys(styles.closures)) {
159
- const current = catalog.closures.get(root);
160
- const next = styles.closures[root];
161
- if (current && !sameComponentStyleClosure(current, next)) {
162
- throw new Error(`[WebUI] Conflicting component style closure "${root}".`);
163
- }
164
- }
165
- }
166
- export function registerPreparedComponentStyles(styles, document = globalThis.document) {
167
- const catalog = catalogFor(document);
168
- for (const id of Object.keys(styles.resources)) {
169
- if (catalog.resources.has(id))
170
- continue;
171
- const resource = styles.resources[id];
172
- catalog.resources.set(id, resource);
173
- }
174
- for (const root of Object.keys(styles.closures)) {
175
- if (!catalog.closures.has(root)) {
176
- catalog.closures.set(root, styles.closures[root]);
177
- }
178
- }
179
- }
180
- export function registerComponentStyles(value, document = globalThis.document) {
181
- const styles = requireComponentStyles(value);
182
- validateComponentStylesRegistration(styles, document);
183
- registerPreparedComponentStyles(styles, document);
184
- }
185
- export function hasRegisteredComponentStyleResource(id, document = globalThis.document) {
186
- return catalogFor(document).resources.has(id);
187
- }
188
- export function hasComponentStyleWork(rootId, document = globalThis.document) {
189
- return (catalogFor(document).closures.get(rootId)?.length ?? 0) > 0;
190
- }
191
40
  function targetInstalledResources(target) {
192
41
  let targetInstalled = installed.get(target);
193
42
  if (!targetInstalled) {
@@ -362,9 +211,7 @@ function installModuleImportMap(target, specifier, css) {
362
211
  const nonce = readNonce(target);
363
212
  if (nonce)
364
213
  script.nonce = nonce;
365
- script.textContent = JSON.stringify({
366
- imports: { [specifier]: `data:text/css,${encodeURIComponent(css)}` },
367
- });
214
+ setImportMapContent(script, specifier, css, owner.defaultView);
368
215
  owner.head.appendChild(script);
369
216
  installedSpecifiers.add(specifier);
370
217
  }
package/dist/lifecycle.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isStreamingHydrationMode } from './streaming-mode.js';
1
2
  let pendingCount = 0;
2
3
  let started = false;
3
4
  let completed = false;
@@ -74,6 +75,9 @@ function tryComplete() {
74
75
  streamingGateAborted ||
75
76
  pendingCount !== 0)
76
77
  return;
78
+ if (!streamingGateActive && hasBrowserDocument && isStreamingHydrationMode()) {
79
+ streamingGateActive = true;
80
+ }
77
81
  if (!started && !streamingGateActive)
78
82
  return;
79
83
  if (streamingGateActive &&
@@ -1,6 +1,6 @@
1
1
  import { consumePendingParentState, TemplateElement, } from './template-element.js';
2
2
  import { hasComponentStyleWork } from './element/styles.js';
3
- import { getTemplateRegistry } from './template.js';
3
+ import { getTemplateRegistry, installTemplateDefinitionPreparation, } from './template.js';
4
4
  import { templateNeedsStaticHost } from './template-roots.js';
5
5
  import { ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
6
6
  import { TEMPLATES_REGISTERED_EVENT, templateRegistrationDetail, } from './template-events.js';
@@ -97,6 +97,7 @@ export function installTemplateElementRuntime() {
97
97
  if (typeof window === 'undefined' || typeof document === 'undefined')
98
98
  return;
99
99
  runtimeInstalled = true;
100
+ installTemplateDefinitionPreparation(null);
100
101
  window.addEventListener(TEMPLATES_REGISTERED_EVENT, (event) => {
101
102
  const detail = templateRegistrationDetail(event);
102
103
  if (!detail?.templates)
@@ -1,5 +1,5 @@
1
- import { registerTemplateData } from './template.js';
2
- import { registerComponentStyles } from './element/styles.js';
1
+ import { registerTemplateData } from './template-registry.js';
2
+ import { registerComponentStyles } from './element/style-catalog.js';
3
3
  export function applyBoundaryBootstrap(bootstrap) {
4
4
  registerComponentStyles(bootstrap.componentStyles);
5
5
  if (bootstrap.templates)
@@ -12,6 +12,8 @@ export function applyBoundaryBootstrap(bootstrap) {
12
12
  const key = keys[i];
13
13
  if (key === 'templates' ||
14
14
  key === 'state' ||
15
+ key === 'stateRef' ||
16
+ key === 'stateDelta' ||
15
17
  key === 'componentStyles' ||
16
18
  key === 'declarationId' ||
17
19
  key === 'enclosingSpanInstanceId')
@@ -6,6 +6,7 @@ import { abandonPendingWaiters, configureStreamingFailureHandler, elementHasPend
6
6
  import { findBoundaryScript, findRangeEndMarkerByPrefix, findRangeStartMarkerByPrefix, markerlessRecordViolation, removeBoundaryScaffolding, resolveBoundaryRange, resolveSpanRange, streamingErrorMessage, } from './streaming-dom.js';
7
7
  import { parseBoundaryEnvelope, RECORD_KIND_FINAL_CHECKPOINT, RECORD_KIND_SPAN_COMPLETION, RECORD_KIND_STATE_UPDATE, RECORD_KIND_TERMINAL, RECORD_KIND_UPDATABLE_CHECKPOINT, } from './streaming-protocol.js';
8
8
  import { applyStateUpdate } from './streaming-state.js';
9
+ import { clearRangeState, resolveRangeState, } from './streaming-record-state.js';
9
10
  import { abandonOpenSpans, completeSpan, hasOpenSpans, openSpanCountForTests, prepareSpanCompletion, registerEnclosingSpans, spanHostFor, } from './streaming-spans.js';
10
11
  const MAX_QUEUED_BOUNDARIES = 512;
11
12
  const MAX_UPDATABLE_BOUNDARIES = 128;
@@ -110,6 +111,7 @@ function fail(reason) {
110
111
  abandonPendingWaiters();
111
112
  abandonOpenSpans();
112
113
  clearUpdatableBoundaries();
114
+ clearRangeState();
113
115
  settlePendingTerminal(false);
114
116
  for (let i = queueHead; i < queue.length; i++) {
115
117
  discardRejectedBoundary(queue[i]);
@@ -151,7 +153,7 @@ function processSentinel(sentinel) {
151
153
  failBoundary(sentinel, parsed.reason);
152
154
  return;
153
155
  }
154
- const [, sequence, kind, target, payload] = parsed.envelope;
156
+ const [sequence, kind, target, payload] = parsed.envelope;
155
157
  if (sequence !== nextExpectedRecordSequence) {
156
158
  failBoundary(sentinel, `expected streaming record sequence ${nextExpectedRecordSequence}, received ${sequence}`);
157
159
  return;
@@ -213,11 +215,12 @@ function commitRecord(span, payload, range, sequence, target, updatable, sentine
213
215
  markBoundaryPending();
214
216
  let committed = false;
215
217
  try {
218
+ const state = resolveRangeState(payload, sequence);
216
219
  if (span) {
217
- hydrateSpanCompletion(payload, range, target);
220
+ hydrateSpanCompletion(payload, state, range, target);
218
221
  }
219
222
  else {
220
- hydrateCheckpoint(payload, range, target, updatable);
223
+ hydrateCheckpoint(payload, state, range, target, updatable);
221
224
  }
222
225
  committed = true;
223
226
  }
@@ -235,7 +238,7 @@ function commitRecord(span, payload, range, sequence, target, updatable, sentine
235
238
  markBoundaryCommitted(false);
236
239
  }
237
240
  }
238
- function hydrateCheckpoint(bootstrap, range, target, updatable) {
241
+ function hydrateCheckpoint(bootstrap, state, range, target, updatable) {
239
242
  let bypass;
240
243
  const enclosing = bootstrap.enclosingSpanInstanceId;
241
244
  if (enclosing !== undefined && range.start?.parentNode) {
@@ -251,7 +254,7 @@ function hydrateCheckpoint(bootstrap, range, target, updatable) {
251
254
  const boundary = updatable
252
255
  ? newUpdatableBoundary()
253
256
  : undefined;
254
- activateRootsBetween(range.start, range.end, bootstrap.state, boundary, bypass);
257
+ activateRootsBetween(range.start, range.end, state, boundary, bypass);
255
258
  if (boundary)
256
259
  retainUpdatableBoundary(target, boundary);
257
260
  }
@@ -262,12 +265,12 @@ function hydrateCheckpoint(bootstrap, range, target, updatable) {
262
265
  function newUpdatableBoundary() {
263
266
  return { roots: [], active: true, retained: 0, pendingRoots: 0 };
264
267
  }
265
- function hydrateSpanCompletion(payload, range, target) {
268
+ function hydrateSpanCompletion(payload, state, range, target) {
266
269
  const invalid = prepareSpanCompletion(target, range);
267
270
  if (invalid)
268
271
  throw new Error(invalid);
269
272
  applyBoundaryBootstrap(payload);
270
- activateRootsBetween(range.start, range.end, payload.state);
273
+ activateRootsBetween(range.start, range.end, state);
271
274
  completeSpan(target);
272
275
  }
273
276
  function failRangeResolution(sentinel, scriptEl, resolved) {
@@ -321,6 +324,7 @@ function commitTerminal(sequence, sentinel, scriptEl) {
321
324
  removeBoundaryScaffolding(sentinel, scriptEl, null, null);
322
325
  terminalCommitted = true;
323
326
  pendingTerminalSequence = sequence;
327
+ clearRangeState();
324
328
  clearUpdatableBoundaries(true);
325
329
  scheduleTerminalValidation();
326
330
  }
@@ -403,6 +407,7 @@ export function resetStreamingCoordinatorStateForTests() {
403
407
  abandonOpenSpans();
404
408
  settlePendingTerminal(false);
405
409
  clearUpdatableBoundaries();
410
+ clearRangeState();
406
411
  coordinatorGeneration++;
407
412
  queue.length = 0;
408
413
  queueHead = 0;