@microsoft/webui-framework 0.0.22 → 0.0.24

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.
@@ -2,7 +2,7 @@ import { deferTemplateDefinition, getTemplate } from './template.js';
2
2
  import { hydrationStart, hydrationEnd } from './lifecycle.js';
3
3
  import { isStreamingHydrationMode, PENDING_ROOT_CONNECTED, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
4
4
  import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
5
- import { collectItemMarkers, nextElement, findByOrdinal, MARKER_COND_START, MARKER_COND_END, MARKER_REPEAT_START, } from './element/markers.js';
5
+ import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, } from './element/markers.js';
6
6
  import { injectModuleStyle, } from './element/styles.js';
7
7
  import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
8
8
  import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
@@ -11,6 +11,15 @@ const templateCache = new WeakMap();
11
11
  const templateDOMCache = new WeakMap();
12
12
  const rootTagCache = new WeakMap();
13
13
  const tplOrdinalCache = new WeakMap();
14
+ const tplElementCache = new WeakMap();
15
+ function getTemplateElements(tplRoot) {
16
+ let cached = tplElementCache.get(tplRoot);
17
+ if (!cached) {
18
+ cached = collectTemplateElements(tplRoot);
19
+ tplElementCache.set(tplRoot, cached);
20
+ }
21
+ return cached;
22
+ }
14
23
  function getTplOrdinals(tplNode) {
15
24
  let map = tplOrdinalCache.get(tplNode);
16
25
  if (map)
@@ -41,6 +50,30 @@ const ACTIVATION_ACTIVATED = 1;
41
50
  const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
42
51
  const ACTIVATION_MISSING_TEMPLATE = 3;
43
52
  const templateMetaByCtor = new WeakMap();
53
+ const pendingAncestorDescendants = new WeakMap();
54
+ const pendingParentStateByElement = new WeakMap();
55
+ function queuePendingParentState(element, name, value, replayAfterHydration) {
56
+ let pending = pendingParentStateByElement.get(element);
57
+ if (!pending) {
58
+ pending = {
59
+ values: Object.create(null),
60
+ };
61
+ pendingParentStateByElement.set(element, pending);
62
+ }
63
+ pending.values[name] = value;
64
+ if (replayAfterHydration) {
65
+ (pending.replay ??= new Set()).add(name);
66
+ }
67
+ }
68
+ function isUnupgradedWebUITarget(element) {
69
+ const tagName = element.localName;
70
+ if (tagName.indexOf('-') === -1)
71
+ return false;
72
+ const ctor = customElements.get(tagName);
73
+ if (ctor)
74
+ return ctor.prototype instanceof TemplateElement;
75
+ return getTemplate(tagName) !== undefined;
76
+ }
44
77
  function childNodesArray(parent) {
45
78
  const children = parent.childNodes;
46
79
  const len = children.length;
@@ -115,7 +148,7 @@ export class TemplateElement extends HTMLElement {
115
148
  this.$root = null;
116
149
  this.$ready = false;
117
150
  this.$hydrated = false;
118
- this.$hydratedCallbackCalled = false;
151
+ this.$hasMounted = false;
119
152
  this.$deferredSSR = false;
120
153
  this.$activatingDeferredSSR = false;
121
154
  this.$hasUnknownScopes = false;
@@ -125,13 +158,16 @@ export class TemplateElement extends HTMLElement {
125
158
  this.$preReadyWrites = null;
126
159
  this.$resolver = (p, s) => this.$resolveValue(p, s);
127
160
  }
161
+ static { this.$ancestorReleaseQueue = []; }
162
+ static { this.$ancestorReleaseIndex = 0; }
163
+ static { this.$ancestorReleaseActive = false; }
128
164
  [WEBUI_SET_STATE_KEY](key, value) {
129
165
  const wasDeferred = this.$deferredSSR;
130
166
  this.$beforeExternalStateWrite();
131
167
  const owned = this.$setStateKey(key, value);
168
+ if (owned && wasDeferred)
169
+ this.$recordDeferredWrite(key);
132
170
  this.$afterExternalStateWrite(owned);
133
- if (owned && wasDeferred && !this.$deferredSSR)
134
- this.$update(key);
135
171
  return owned;
136
172
  }
137
173
  [STREAMING_BOUNDARY_ACTIVATE](state) {
@@ -148,14 +184,35 @@ export class TemplateElement extends HTMLElement {
148
184
  this.$meta = meta;
149
185
  if (!this.$shouldActivateOnBoundaryCommit())
150
186
  return ACTIVATION_STATIC_HOST_OPT_OUT;
151
- this.$activateDeferredSSR(state);
187
+ const ancestor = this.$nearestHydrationBarrier();
188
+ if (ancestor) {
189
+ this.$deferredByAncestor = true;
190
+ this.$ancestorBoundaryState = state;
191
+ this.$hasAncestorBoundaryState = true;
192
+ this.$registerWithHydrationBarrier(ancestor);
193
+ return ACTIVATION_ACTIVATED;
194
+ }
195
+ this.$activatingDeferredSSR = true;
196
+ try {
197
+ this.$activateDeferredSSR(state);
198
+ }
199
+ finally {
200
+ this.$activatingDeferredSSR = false;
201
+ }
152
202
  return ACTIVATION_ACTIVATED;
153
203
  }
154
204
  [STREAMING_BOUNDARY_ABANDON]() {
205
+ this.$detachDeferredAncestor();
206
+ this.$abandonDeferredDescendants();
155
207
  this.$deferredSSR = false;
208
+ this.$deferredByAncestor = undefined;
209
+ this.$ancestorBoundaryState = undefined;
210
+ this.$hasAncestorBoundaryState = undefined;
156
211
  this.$activatingDeferredSSR = false;
157
212
  this.$ready = false;
158
213
  this.$preReadyWrites = null;
214
+ if (this.$deferredWrites)
215
+ this.$deferredWrites = undefined;
159
216
  }
160
217
  static define(tagName) {
161
218
  const ctor = this;
@@ -170,8 +227,20 @@ export class TemplateElement extends HTMLElement {
170
227
  }
171
228
  connectedCallback() {
172
229
  const tag = this.tagName.toLowerCase();
230
+ this.$adoptPendingDescendants();
173
231
  if (this.$deferredSSR) {
174
232
  this.$ready = true;
233
+ if (this.$deferredByAncestor) {
234
+ const ancestor = this.$nearestHydrationBarrier();
235
+ if (ancestor) {
236
+ this.$registerWithHydrationBarrier(ancestor);
237
+ return;
238
+ }
239
+ this.$detachDeferredAncestor();
240
+ this.$releaseAncestorBarrier();
241
+ return;
242
+ }
243
+ this.$didDeferSSRHydration();
175
244
  return;
176
245
  }
177
246
  if (this.$hydrated && this.$root) {
@@ -185,12 +254,15 @@ export class TemplateElement extends HTMLElement {
185
254
  }
186
255
  return;
187
256
  }
188
- if (isStreamingHydrationMode() && this.hasAttribute(STREAMED_HOST_ATTR)) {
257
+ if (!this.$hasMounted &&
258
+ isStreamingHydrationMode() &&
259
+ this.hasAttribute(STREAMED_HOST_ATTR)) {
189
260
  this.$deferredSSR = true;
190
261
  this.$ready = true;
191
262
  const resume = this[PENDING_ROOT_CONNECTED];
192
263
  if (typeof resume === 'function')
193
264
  resume.call(this);
265
+ this.$didDeferSSRHydration();
194
266
  return;
195
267
  }
196
268
  const meta = this.$templateMeta();
@@ -200,17 +272,35 @@ export class TemplateElement extends HTMLElement {
200
272
  return;
201
273
  }
202
274
  this.$meta = meta;
203
- this.$mount(meta, false);
275
+ if (this.$hasMounted) {
276
+ this.$remount(meta);
277
+ }
278
+ else {
279
+ this.$mount(meta, false);
280
+ }
281
+ }
282
+ $remount(meta) {
283
+ this.$mount(meta, false, undefined, false, true);
204
284
  }
205
- $mount(meta, forceSSR, ssrState) {
285
+ $mount(meta, forceSSR, ssrState, hasBoundaryState = false, reconnecting = false) {
206
286
  if (this.$hydrated)
207
287
  return;
208
288
  const hasShadow = !!this.shadowRoot;
209
289
  const wantShadow = hasShadow || !!meta.sd;
290
+ const remountStructuralTemplate = reconnecting &&
291
+ ((meta.c?.length ?? 0) !== 0 || (meta.r?.length ?? 0) !== 0);
210
292
  let root;
211
293
  let isSSR;
212
294
  let clientRoot = null;
213
- if (hasShadow) {
295
+ if (remountStructuralTemplate) {
296
+ const renderRoot = wantShadow
297
+ ? this.shadowRoot ?? this.attachShadow({ mode: 'open' })
298
+ : this;
299
+ renderRoot.replaceChildren();
300
+ root = renderRoot;
301
+ isSSR = false;
302
+ }
303
+ else if (hasShadow) {
214
304
  root = this.shadowRoot;
215
305
  isSSR = true;
216
306
  }
@@ -226,20 +316,42 @@ export class TemplateElement extends HTMLElement {
226
316
  root = this;
227
317
  isSSR = false;
228
318
  }
229
- if (isSSR && !forceSSR && this.$shouldDeferSSRHydration()) {
230
- this.$meta = meta;
231
- this.$deferredSSR = true;
232
- this.$ready = true;
233
- return;
319
+ if (isSSR &&
320
+ !forceSSR &&
321
+ !reconnecting) {
322
+ const ancestor = this.$nearestHydrationBarrier();
323
+ if (ancestor) {
324
+ this.$meta = meta;
325
+ this.$deferredSSR = true;
326
+ this.$deferredByAncestor = true;
327
+ this.$ready = true;
328
+ this.$primeSSRStateForDeferral();
329
+ this.$registerWithHydrationBarrier(ancestor);
330
+ return;
331
+ }
332
+ if (!this.$shouldDeferSSRHydration(meta)) {
333
+ }
334
+ else {
335
+ this.$meta = meta;
336
+ this.$deferredSSR = true;
337
+ this.$ready = true;
338
+ this.$didDeferSSRHydration();
339
+ return;
340
+ }
234
341
  }
235
342
  hydrationStart();
236
343
  try {
237
344
  if (meta.sa)
238
345
  injectModuleStyle(meta.sa, this.shadowRoot);
239
- if (isSSR) {
240
- if (this.$shouldApplySSRBootstrapState()) {
241
- this.$applySSRState(forceSSR ? ssrState : window.__webui?.state);
346
+ if (!reconnecting) {
347
+ if (isSSR) {
348
+ if (this.$shouldApplySSRBootstrapState()) {
349
+ this.$applySSRState(hasBoundaryState ? ssrState : window.__webui?.state);
350
+ }
242
351
  }
352
+ this.$applyPendingParentState(isSSR);
353
+ }
354
+ if (isSSR) {
243
355
  this.$root = this.$hydrate(root, meta, getTemplateDom(meta));
244
356
  }
245
357
  else {
@@ -250,6 +362,11 @@ export class TemplateElement extends HTMLElement {
250
362
  this.$hydrated = true;
251
363
  this.$ready = true;
252
364
  this.$syncAuthoredAttributes();
365
+ if (isSSR && this.$deferredWrites)
366
+ this.$replayDeferredWrites();
367
+ if (isSSR && reconnecting) {
368
+ this.$updateBindings(this.$root.texts, this.$root.attrs, this.$root.conds, this.$root.repeats, true);
369
+ }
253
370
  if (isSSR && DEV)
254
371
  this.$checkHydrationMismatch();
255
372
  else
@@ -263,13 +380,14 @@ export class TemplateElement extends HTMLElement {
263
380
  this.$appendStagedChildren(root, clientRoot);
264
381
  this.$root.container = root;
265
382
  }
266
- this.$notifyHydrated();
383
+ this.$finishHydration();
267
384
  }
268
385
  finally {
269
386
  hydrationEnd();
270
387
  }
271
388
  }
272
389
  disconnectedCallback() {
390
+ this.$detachDeferredAncestor();
273
391
  if (this.$root) {
274
392
  queueMicrotask(() => {
275
393
  if (!this.isConnected)
@@ -279,9 +397,16 @@ export class TemplateElement extends HTMLElement {
279
397
  }
280
398
  $destroy() {
281
399
  if (!this.$root) {
400
+ this.$detachDeferredAncestor();
401
+ this.$abandonDeferredDescendants();
282
402
  this.$deferredSSR = false;
403
+ this.$deferredByAncestor = undefined;
404
+ this.$ancestorBoundaryState = undefined;
405
+ this.$hasAncestorBoundaryState = undefined;
283
406
  this.$ready = false;
284
407
  this.$hasUnknownScopes = false;
408
+ if (this.$deferredWrites)
409
+ this.$deferredWrites = undefined;
285
410
  return;
286
411
  }
287
412
  this.$disposeInstance(this.$root, false);
@@ -291,6 +416,7 @@ export class TemplateElement extends HTMLElement {
291
416
  this.$dirtyPaths = null;
292
417
  this.$pendingFlush = false;
293
418
  this.$preReadyWrites = null;
419
+ this.$hydrated = false;
294
420
  this.$ready = false;
295
421
  this.$hasUnknownScopes = false;
296
422
  }
@@ -324,11 +450,11 @@ export class TemplateElement extends HTMLElement {
324
450
  const property = this.$templateRootForAttribute(name);
325
451
  let changed = false;
326
452
  if (property && this.$usesTemplateState(property)) {
453
+ if (wasDeferred)
454
+ this.$recordDeferredWrite(property);
327
455
  changed = this.$setTemplateState(property, newValue);
328
456
  }
329
457
  this.$afterExternalStateWrite(changed);
330
- if (changed && property && wasDeferred && !this.$deferredSSR)
331
- this.$update(property);
332
458
  }
333
459
  setState(state) {
334
460
  const wasDeferred = this.$deferredSSR;
@@ -337,13 +463,12 @@ export class TemplateElement extends HTMLElement {
337
463
  let owned = false;
338
464
  for (let i = 0; i < keys.length; i++) {
339
465
  const key = keys[i];
340
- owned = this.$setStateKey(key, state[key]) || owned;
466
+ const keyOwned = this.$setStateKey(key, state[key]);
467
+ if (keyOwned && wasDeferred)
468
+ this.$recordDeferredWrite(key);
469
+ owned = keyOwned || owned;
341
470
  }
342
471
  this.$afterExternalStateWrite(owned);
343
- if (owned && wasDeferred && !this.$deferredSSR) {
344
- for (let i = 0; i < keys.length; i++)
345
- this.$update(keys[i]);
346
- }
347
472
  this.$flushUpdates();
348
473
  }
349
474
  $observableNames() {
@@ -353,27 +478,221 @@ export class TemplateElement extends HTMLElement {
353
478
  }
354
479
  $afterExternalStateWrite(_applied) {
355
480
  }
356
- $shouldDeferSSRHydration() {
481
+ $shouldDeferSSRHydration(_meta) {
357
482
  return false;
358
483
  }
484
+ $didDeferSSRHydration() {
485
+ }
486
+ $didDeferStreamedSSRHydration(_state) {
487
+ this.$didDeferSSRHydration();
488
+ }
489
+ $currentTemplateMetadata() {
490
+ return this.$meta ?? this.$templateMeta();
491
+ }
492
+ $nearestHydrationBarrier() {
493
+ let current = this;
494
+ while (true) {
495
+ let parent = current.assignedSlot ?? current.parentElement;
496
+ if (!parent) {
497
+ const getRootNode = current.getRootNode;
498
+ const root = typeof getRootNode === 'function'
499
+ ? getRootNode.call(current)
500
+ : null;
501
+ parent = typeof ShadowRoot !== 'undefined' &&
502
+ root instanceof ShadowRoot
503
+ ? root.host
504
+ : null;
505
+ }
506
+ if (!parent)
507
+ return undefined;
508
+ if (parent instanceof TemplateElement) {
509
+ const parentMeta = parent.$meta ?? parent.$templateMeta();
510
+ if (parentMeta?.th) {
511
+ current = parent;
512
+ continue;
513
+ }
514
+ if (parent.$deferredSSR || !parent.$hydrated)
515
+ return parent;
516
+ }
517
+ else {
518
+ const parentMeta = getTemplate(parent.tagName.toLowerCase());
519
+ if (parentMeta && !parentMeta.th)
520
+ return parent;
521
+ }
522
+ current = parent;
523
+ }
524
+ }
525
+ $registerWithHydrationBarrier(ancestor) {
526
+ if (ancestor instanceof TemplateElement) {
527
+ ancestor.$registerDeferredDescendant(this);
528
+ return;
529
+ }
530
+ this.$detachDeferredAncestor();
531
+ this.$pendingAncestor = ancestor;
532
+ const descendants = pendingAncestorDescendants.get(ancestor);
533
+ if (descendants) {
534
+ descendants.push(this);
535
+ }
536
+ else {
537
+ pendingAncestorDescendants.set(ancestor, [this]);
538
+ }
539
+ }
540
+ $adoptPendingDescendants() {
541
+ const descendants = pendingAncestorDescendants.get(this);
542
+ if (!descendants)
543
+ return;
544
+ pendingAncestorDescendants.delete(this);
545
+ for (let i = 0; i < descendants.length; i++) {
546
+ const descendant = descendants[i];
547
+ if (descendant.$pendingAncestor !== this)
548
+ continue;
549
+ descendant.$pendingAncestor = undefined;
550
+ if (descendant.isConnected)
551
+ this.$registerDeferredDescendant(descendant);
552
+ }
553
+ }
554
+ $registerDeferredDescendant(descendant) {
555
+ if (descendant.$deferredAncestor === this)
556
+ return;
557
+ descendant.$detachDeferredAncestor();
558
+ descendant.$deferredAncestor = this;
559
+ (this.$deferredDescendants ??= []).push(descendant);
560
+ }
561
+ $detachDeferredAncestor() {
562
+ const pendingAncestor = this.$pendingAncestor;
563
+ if (pendingAncestor) {
564
+ this.$pendingAncestor = undefined;
565
+ const pending = pendingAncestorDescendants.get(pendingAncestor);
566
+ if (pending) {
567
+ const pendingIndex = pending.indexOf(this);
568
+ if (pendingIndex >= 0)
569
+ pending.splice(pendingIndex, 1);
570
+ if (pending.length === 0) {
571
+ pendingAncestorDescendants.delete(pendingAncestor);
572
+ }
573
+ }
574
+ }
575
+ const ancestor = this.$deferredAncestor;
576
+ if (!ancestor)
577
+ return;
578
+ this.$deferredAncestor = undefined;
579
+ const descendants = ancestor.$deferredDescendants;
580
+ if (!descendants)
581
+ return;
582
+ const index = descendants.indexOf(this);
583
+ if (index >= 0)
584
+ descendants.splice(index, 1);
585
+ if (descendants.length === 0)
586
+ ancestor.$deferredDescendants = undefined;
587
+ }
588
+ $releaseDeferredDescendants() {
589
+ const descendants = this.$deferredDescendants;
590
+ if (!descendants)
591
+ return;
592
+ this.$deferredDescendants = undefined;
593
+ const queue = TemplateElement.$ancestorReleaseQueue;
594
+ for (let i = 0; i < descendants.length; i++) {
595
+ const descendant = descendants[i];
596
+ if (descendant.$deferredAncestor !== this)
597
+ continue;
598
+ descendant.$deferredAncestor = undefined;
599
+ queue.push(descendant);
600
+ }
601
+ if (TemplateElement.$ancestorReleaseActive)
602
+ return;
603
+ TemplateElement.$ancestorReleaseActive = true;
604
+ let errors;
605
+ try {
606
+ while (TemplateElement.$ancestorReleaseIndex < queue.length) {
607
+ const descendant = queue[TemplateElement.$ancestorReleaseIndex];
608
+ TemplateElement.$ancestorReleaseIndex++;
609
+ try {
610
+ descendant.$releaseAncestorBarrier();
611
+ }
612
+ catch (error) {
613
+ (errors ??= []).push(error);
614
+ }
615
+ }
616
+ }
617
+ finally {
618
+ queue.length = 0;
619
+ TemplateElement.$ancestorReleaseIndex = 0;
620
+ TemplateElement.$ancestorReleaseActive = false;
621
+ }
622
+ if (errors) {
623
+ if (errors.length === 1)
624
+ throw errors[0];
625
+ throw new AggregateError(errors, 'multiple deferred descendants failed to hydrate');
626
+ }
627
+ }
628
+ $releaseAncestorBarrier() {
629
+ if (!this.$deferredByAncestor || !this.isConnected)
630
+ return;
631
+ this.$deferredByAncestor = undefined;
632
+ const hasBoundaryState = this.$hasAncestorBoundaryState === true;
633
+ const boundaryState = this.$ancestorBoundaryState;
634
+ this.$hasAncestorBoundaryState = undefined;
635
+ this.$ancestorBoundaryState = undefined;
636
+ const meta = this.$meta;
637
+ if (meta && this.$shouldDeferSSRHydration(meta)) {
638
+ if (hasBoundaryState) {
639
+ this.$didDeferStreamedSSRHydration(boundaryState);
640
+ }
641
+ else {
642
+ this.$didDeferSSRHydration();
643
+ }
644
+ return;
645
+ }
646
+ if (hasBoundaryState) {
647
+ this.$activateDeferredSSRFromBoundary(boundaryState);
648
+ }
649
+ else {
650
+ this.$activateDeferredSSR();
651
+ }
652
+ }
653
+ $abandonDeferredDescendants() {
654
+ const descendants = this.$deferredDescendants;
655
+ if (!descendants)
656
+ return;
657
+ this.$deferredDescendants = undefined;
658
+ for (let i = 0; i < descendants.length; i++) {
659
+ if (descendants[i].$deferredAncestor === this) {
660
+ descendants[i].$deferredAncestor = undefined;
661
+ }
662
+ }
663
+ }
664
+ $primeSSRStateForDeferral() {
665
+ if (this.$shouldApplySSRBootstrapState()) {
666
+ this.$applySSRState(window.__webui?.state);
667
+ }
668
+ this.$applyPendingParentState(true);
669
+ }
359
670
  $shouldActivateOnBoundaryCommit() {
360
671
  return true;
361
672
  }
362
673
  $activateDeferredSSR(state) {
674
+ this.$activateDeferredSSRWithState(state, this.$activatingDeferredSSR);
675
+ }
676
+ $activateDeferredSSRFromBoundary(state) {
677
+ this.$activateDeferredSSRWithState(state, true);
678
+ }
679
+ $activateDeferredSSRWithState(state, hasBoundaryState) {
363
680
  if (!this.$deferredSSR)
364
681
  return;
365
682
  const meta = this.$meta;
366
683
  if (!meta)
367
684
  return;
368
685
  this.$deferredSSR = false;
686
+ this.$guardUnknownState = true;
369
687
  this.$ready = false;
370
688
  this.$preReadyWrites = null;
689
+ const wasActivating = this.$activatingDeferredSSR;
371
690
  this.$activatingDeferredSSR = true;
372
691
  try {
373
- this.$mount(meta, true, state);
692
+ this.$mount(meta, true, state, hasBoundaryState);
374
693
  }
375
694
  finally {
376
- this.$activatingDeferredSSR = false;
695
+ this.$activatingDeferredSSR = wasActivating;
377
696
  }
378
697
  }
379
698
  $shouldApplySSRBootstrapState() {
@@ -387,11 +706,33 @@ export class TemplateElement extends HTMLElement {
387
706
  hydratedCallback() {
388
707
  }
389
708
  $notifyHydrated() {
390
- if (this.$hydratedCallbackCalled)
709
+ if (this.$hasMounted)
391
710
  return;
392
- this.$hydratedCallbackCalled = true;
711
+ this.$hasMounted = true;
393
712
  this.hydratedCallback();
394
713
  }
714
+ $finishHydration() {
715
+ let callbackFailed = false;
716
+ let callbackError;
717
+ try {
718
+ this.$notifyHydrated();
719
+ }
720
+ catch (error) {
721
+ callbackFailed = true;
722
+ callbackError = error;
723
+ }
724
+ try {
725
+ this.$releaseDeferredDescendants();
726
+ }
727
+ catch (releaseError) {
728
+ if (callbackFailed) {
729
+ throw new AggregateError([callbackError, releaseError], 'component callback and deferred descendant hydration failed');
730
+ }
731
+ throw releaseError;
732
+ }
733
+ if (callbackFailed)
734
+ throw callbackError;
735
+ }
395
736
  $shouldApplyTemplateStateFromSSR(_key) {
396
737
  return true;
397
738
  }
@@ -406,7 +747,8 @@ export class TemplateElement extends HTMLElement {
406
747
  if (!this.$templateState) {
407
748
  this.$templateState = Object.create(null);
408
749
  }
409
- if (Object.is(this.$templateState[key], value))
750
+ if (Object.prototype.hasOwnProperty.call(this.$templateState, key) &&
751
+ Object.is(this.$templateState[key], value))
410
752
  return false;
411
753
  this.$templateState[key] = value;
412
754
  return true;
@@ -443,9 +785,12 @@ export class TemplateElement extends HTMLElement {
443
785
  if (!state || typeof state !== 'object')
444
786
  return;
445
787
  const observableNames = this.$observableNames();
788
+ const deferredWrites = this.$deferredWrites;
446
789
  const keys = Object.keys(state);
447
790
  for (let i = 0; i < keys.length; i++) {
448
791
  const key = keys[i];
792
+ if (deferredWrites?.has(key))
793
+ continue;
449
794
  if (observableNames.has(key)) {
450
795
  if (!this.$shouldApplySSRState(key))
451
796
  continue;
@@ -456,8 +801,42 @@ export class TemplateElement extends HTMLElement {
456
801
  }
457
802
  }
458
803
  }
804
+ $applyPendingParentState(replayAfterHydration) {
805
+ const pending = pendingParentStateByElement.get(this);
806
+ if (!pending)
807
+ return;
808
+ pendingParentStateByElement.delete(this);
809
+ const state = pending.values;
810
+ const observableNames = this.$observableNames();
811
+ const keys = Object.keys(state);
812
+ for (let i = 0; i < keys.length; i++) {
813
+ const key = keys[i];
814
+ if (observableNames.has(key)) {
815
+ this[`_${key}`] = state[key];
816
+ }
817
+ else if (this.$usesTemplateState(key)) {
818
+ this.$writeTemplateState(key, state[key]);
819
+ }
820
+ else {
821
+ this[key] = state[key];
822
+ }
823
+ }
824
+ const replay = pending.replay;
825
+ if (replayAfterHydration && replay) {
826
+ const deferredWrites = this.$deferredWrites;
827
+ if (deferredWrites) {
828
+ for (const key of replay)
829
+ deferredWrites.add(key);
830
+ }
831
+ else {
832
+ this.$deferredWrites = replay;
833
+ }
834
+ }
835
+ }
459
836
  $update(path) {
460
837
  if (!this.$ready || !this.$root) {
838
+ if (path && this.$deferredSSR)
839
+ this.$recordDeferredWrite(path);
461
840
  if (DEV && path && this.isConnected)
462
841
  this.$recordPreReadyWrite(path);
463
842
  return;
@@ -480,11 +859,31 @@ export class TemplateElement extends HTMLElement {
480
859
  this.$dirtyPaths = null;
481
860
  this.$updateInstance(this.$root);
482
861
  }
862
+ $recordDeferredWrite(path) {
863
+ const dot = path.indexOf('.');
864
+ const root = dot === -1 ? path : path.slice(0, dot);
865
+ (this.$deferredWrites ??= new Set()).add(root);
866
+ }
867
+ $replayDeferredWrites() {
868
+ const writes = this.$deferredWrites;
869
+ if (!writes)
870
+ return;
871
+ this.$deferredWrites = undefined;
872
+ if (this.$dirtyPaths) {
873
+ for (const path of writes)
874
+ this.$dirtyPaths.add(path);
875
+ }
876
+ else {
877
+ this.$dirtyPaths = writes;
878
+ }
879
+ this.$pendingFlush = true;
880
+ this.$flush(true);
881
+ }
483
882
  $flushUpdates() {
484
883
  if (this.$pendingFlush)
485
884
  this.$flush();
486
885
  }
487
- $flush() {
886
+ $flush(requireKnownState = this.$guardUnknownState === true) {
488
887
  if (!this.$ready || !this.$root) {
489
888
  this.$dirtyPaths = null;
490
889
  this.$pendingFlush = false;
@@ -502,14 +901,14 @@ export class TemplateElement extends HTMLElement {
502
901
  this.$buildPathIndex();
503
902
  const entry = this.$pathIndex?.get(path);
504
903
  if (entry) {
505
- this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats);
904
+ this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats, requireKnownState);
506
905
  }
507
906
  }
508
907
  if (!this.$pathIndex)
509
908
  this.$buildPathIndex();
510
909
  if (this.$wildcardBindings) {
511
910
  const wc = this.$wildcardBindings;
512
- this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats);
911
+ this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats, requireKnownState);
513
912
  }
514
913
  if (!this.$pathIndex)
515
914
  this.$buildPathIndex();
@@ -541,49 +940,6 @@ export class TemplateElement extends HTMLElement {
541
940
  const tag = this.tagName.toLowerCase();
542
941
  void import('./hydration-mismatch.js').then((m) => m.reportHydrationMismatch(tag, writes, index, ctx));
543
942
  }
544
- $resolve(root, path, pathStart = 0) {
545
- let cur = root;
546
- for (let i = 0; i < pathStart; i++) {
547
- const child = cur.childNodes[path[i]];
548
- if (!child)
549
- return null;
550
- cur = child;
551
- }
552
- for (let i = pathStart; i < path.length; i++) {
553
- const child = cur.childNodes[path[i]];
554
- if (!child)
555
- return null;
556
- cur = child;
557
- }
558
- return cur;
559
- }
560
- $resolveSSR(ssrRoot, tplRoot, path, pathStart = 0) {
561
- let ssr = ssrRoot;
562
- let tpl = tplRoot;
563
- for (let i = 0; i < pathStart; i++) {
564
- const tplChild = tpl.childNodes[path[i]];
565
- if (!tplChild)
566
- return null;
567
- tpl = tplChild;
568
- }
569
- for (let i = pathStart; i < path.length; i++) {
570
- const idx = path[i];
571
- const tplChild = tpl.childNodes[idx];
572
- if (!tplChild)
573
- return null;
574
- const ordinals = getTplOrdinals(tpl);
575
- const entry = ordinals.get(idx);
576
- if (!entry)
577
- return null;
578
- const [nodeType, ordinal] = entry;
579
- const child = findByOrdinal(ssr, nodeType, ordinal);
580
- if (!child)
581
- return null;
582
- ssr = child;
583
- tpl = tplChild;
584
- }
585
- return ssr;
586
- }
587
943
  $parseTemplate(meta) {
588
944
  let cached = templateCache.get(meta);
589
945
  if (cached)
@@ -644,6 +1000,7 @@ export class TemplateElement extends HTMLElement {
644
1000
  scope, container: root, nodes: childNodesArray(root),
645
1001
  texts: [], attrs: [], conds: [], repeats: [],
646
1002
  };
1003
+ const elements = collectTemplateElements(root);
647
1004
  const textRefs = new Array(meta.tx?.length ?? 0);
648
1005
  let textRefCount = 0;
649
1006
  if (meta.tx) {
@@ -651,8 +1008,8 @@ export class TemplateElement extends HTMLElement {
651
1008
  const entry = meta.tx[i];
652
1009
  const [slot, parts] = entry;
653
1010
  const raw = entry[2] === 1;
654
- const [parentPath, beforeIndex] = slot;
655
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
1011
+ const [parentIndex, beforeIndex] = slot;
1012
+ const parent = elements[parentIndex];
656
1013
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
657
1014
  continue;
658
1015
  textRefs[textRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, parts, raw };
@@ -664,8 +1021,8 @@ export class TemplateElement extends HTMLElement {
664
1021
  if (meta.c) {
665
1022
  for (let i = 0; i < meta.c.length; i++) {
666
1023
  const [condition, blockIndex, slotMeta] = meta.c[i];
667
- const [parentPath, beforeIndex] = slotMeta;
668
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
1024
+ const [parentIndex, beforeIndex] = slotMeta;
1025
+ const parent = elements[parentIndex];
669
1026
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
670
1027
  continue;
671
1028
  condRefs[condRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, condition: condition, blockIndex };
@@ -677,8 +1034,8 @@ export class TemplateElement extends HTMLElement {
677
1034
  if (meta.r) {
678
1035
  for (let i = 0; i < meta.r.length; i++) {
679
1036
  const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
680
- const [parentPath, beforeIndex] = slotMeta;
681
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
1037
+ const [parentIndex, beforeIndex] = slotMeta;
1038
+ const parent = elements[parentIndex];
682
1039
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
683
1040
  continue;
684
1041
  repRefs[repRefCount] = {
@@ -692,8 +1049,8 @@ export class TemplateElement extends HTMLElement {
692
1049
  repRefCount += 1;
693
1050
  }
694
1051
  }
695
- this.$wireAttrs(instance, meta, scope, (p) => this.$resolve(root, p));
696
- this.$finalize(instance, root, meta, (r, p) => this.$resolve(r, p), scope);
1052
+ this.$wireAttrs(instance, meta, scope, (i) => elements[i] ?? null);
1053
+ this.$finalize(instance, root, meta, (_r, i) => elements[i] ?? null, scope);
697
1054
  for (let i = 0; i < textRefCount; i++) {
698
1055
  const t = textRefs[i];
699
1056
  const anchor = document.createComment('');
@@ -749,16 +1106,19 @@ export class TemplateElement extends HTMLElement {
749
1106
  texts: [], attrs: [], conds: [], repeats: [],
750
1107
  };
751
1108
  const staleMarkers = [];
1109
+ const ssrIndex = buildSSRIndex(tplDom, ssrRoot, meta.c !== undefined || meta.r !== undefined, pathStart > 0);
1110
+ const ssrElements = ssrIndex.elements;
1111
+ const tplElements = getTemplateElements(tplDom);
752
1112
  if (meta.tx) {
753
1113
  for (let i = 0; i < meta.tx.length; i++) {
754
1114
  const entry = meta.tx[i];
755
1115
  const [slot, parts] = entry;
756
1116
  const raw = entry[2] === 1;
757
- const [parentPath, beforeIndex] = slot;
758
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart);
1117
+ const [parentIndex, beforeIndex] = slot;
1118
+ const ssrParent = ssrElements[parentIndex];
759
1119
  if (!ssrParent)
760
1120
  continue;
761
- const tplParent = this.$resolve(tplDom, parentPath, pathStart);
1121
+ const tplParent = tplElements[parentIndex];
762
1122
  if (!tplParent)
763
1123
  continue;
764
1124
  if (raw) {
@@ -778,21 +1138,16 @@ export class TemplateElement extends HTMLElement {
778
1138
  }
779
1139
  }
780
1140
  }
781
- this.$wireAttrs(instance, meta, scope, (p) => this.$resolveSSR(ssrRoot, tplDom, p, pathStart));
1141
+ this.$wireAttrs(instance, meta, scope, (i) => ssrElements[i], true);
782
1142
  if (meta.c) {
783
- let lastCondMarker = null;
784
- let lastCondParent = null;
1143
+ const condMarkers = ssrIndex.conds.length === meta.c.length ? ssrIndex.conds : null;
785
1144
  for (let i = 0; i < meta.c.length; i++) {
786
1145
  const [condition, blockIndex, slotMeta] = meta.c[i];
787
- const [parentPath] = slotMeta;
788
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
1146
+ const [parentIndex] = slotMeta;
789
1147
  const blockMeta = this.$block(blockIndex);
790
1148
  let condInstance = null;
791
- if (ssrParent !== lastCondParent) {
792
- lastCondMarker = null;
793
- lastCondParent = ssrParent;
794
- }
795
- const marker = this.$findMarker(ssrParent, MARKER_COND_START, lastCondMarker);
1149
+ const marker = condMarkers ? condMarkers[i] : null;
1150
+ const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
796
1151
  let condAnchor;
797
1152
  if (marker) {
798
1153
  condAnchor = marker;
@@ -803,8 +1158,6 @@ export class TemplateElement extends HTMLElement {
803
1158
  const insertRef = ssrParent.childNodes[beforeIndex ?? ssrParent.childNodes.length] ?? null;
804
1159
  ssrParent.insertBefore(condAnchor, insertRef);
805
1160
  }
806
- if (marker)
807
- lastCondMarker = marker;
808
1161
  if (marker && blockMeta) {
809
1162
  condInstance = this.$hydrateCondContent(condAnchor, blockMeta, scope);
810
1163
  if (condInstance)
@@ -825,22 +1178,17 @@ export class TemplateElement extends HTMLElement {
825
1178
  }
826
1179
  }
827
1180
  if (meta.r) {
828
- let lastRepMarker = null;
829
- let lastRepParent = null;
1181
+ const repMarkers = ssrIndex.repeats.length === meta.r.length ? ssrIndex.repeats : null;
830
1182
  for (let i = 0; i < meta.r.length; i++) {
831
1183
  const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
832
- const [parentPath] = slotMeta;
833
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
834
- if (ssrParent !== lastRepParent) {
835
- lastRepMarker = null;
836
- lastRepParent = ssrParent;
837
- }
1184
+ const [parentIndex] = slotMeta;
1185
+ const marker = repMarkers ? repMarkers[i] : null;
1186
+ const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
838
1187
  const blockMeta = this.$block(blockIndex);
839
1188
  const blockTplDom = blockMeta ? getTemplateDom(blockMeta) : null;
840
1189
  const rootTag = blockMeta && blockTplDom?.childNodes.length === 1 && blockTplDom.children.length === 1
841
1190
  ? this.$rootTag(blockMeta)
842
1191
  : null;
843
- const marker = this.$findMarker(ssrParent, MARKER_REPEAT_START, lastRepMarker);
844
1192
  let anchor;
845
1193
  if (marker) {
846
1194
  anchor = marker;
@@ -848,7 +1196,7 @@ export class TemplateElement extends HTMLElement {
848
1196
  else {
849
1197
  anchor = document.createComment('');
850
1198
  const [, beforeIndex] = slotMeta;
851
- const tplParent = this.$resolve(tplDom, parentPath, pathStart);
1199
+ const tplParent = tplElements[parentIndex];
852
1200
  const staticCount = tplParent ? tplParent.childNodes.length : 0;
853
1201
  const insertRef = ssrParent.childNodes[Math.min(beforeIndex ?? staticCount, ssrParent.childNodes.length)] ?? null;
854
1202
  ssrParent.insertBefore(anchor, insertRef);
@@ -860,7 +1208,6 @@ export class TemplateElement extends HTMLElement {
860
1208
  const { items: itemMarkers, end: endMarker } = marker
861
1209
  ? collectItemMarkers(anchor)
862
1210
  : { items: [], end: null };
863
- lastRepMarker = endMarker ?? anchor;
864
1211
  if (blockMeta && blockTplDom && anchor.parentNode && itemMarkers.length > 0) {
865
1212
  if (!this.$activatingDeferredSSR
866
1213
  && hasCollectionState
@@ -933,7 +1280,7 @@ export class TemplateElement extends HTMLElement {
933
1280
  instance.repeats.push(binding);
934
1281
  }
935
1282
  }
936
- this.$finalize(instance, ssrRoot, meta, (r, p) => this.$resolveSSR(r, tplDom, p, pathStart), scope);
1283
+ this.$finalize(instance, ssrRoot, meta, (_r, i) => ssrElements[i] ?? null, scope);
937
1284
  for (let i = 0; i < staleMarkers.length; i++) {
938
1285
  staleMarkers[i].parentNode?.removeChild(staleMarkers[i]);
939
1286
  }
@@ -964,13 +1311,13 @@ export class TemplateElement extends HTMLElement {
964
1311
  $hasRootStructuralSlot(meta) {
965
1312
  if (meta.c) {
966
1313
  for (let i = 0; i < meta.c.length; i++) {
967
- if (meta.c[i][2][0].length === 0)
1314
+ if (meta.c[i][2][0] === 0)
968
1315
  return true;
969
1316
  }
970
1317
  }
971
1318
  if (meta.r) {
972
1319
  for (let i = 0; i < meta.r.length; i++) {
973
- if (meta.r[i][3][0].length === 0)
1320
+ if (meta.r[i][3][0] === 0)
974
1321
  return true;
975
1322
  }
976
1323
  }
@@ -1004,16 +1351,6 @@ export class TemplateElement extends HTMLElement {
1004
1351
  this.$replaceInstanceContainer(inst, wrapper, container);
1005
1352
  return inst;
1006
1353
  }
1007
- $findMarker(parent, data, after) {
1008
- let child = after ? after.nextSibling : parent.firstChild;
1009
- while (child) {
1010
- if (child.nodeType === 8 && child.data === data) {
1011
- return child;
1012
- }
1013
- child = child.nextSibling;
1014
- }
1015
- return null;
1016
- }
1017
1354
  $findSSRText(ssrParent, tplParent, beforeIndex) {
1018
1355
  const ordinals = getTplOrdinals(tplParent);
1019
1356
  let textOrd = 0;
@@ -1065,20 +1402,51 @@ export class TemplateElement extends HTMLElement {
1065
1402
  rootTagCache.set(meta, tag);
1066
1403
  return tag;
1067
1404
  }
1068
- $wireAttrs(instance, meta, scope, resolve) {
1405
+ $wireAttrs(instance, meta, scope, resolve, primeSSRComplexProperties = false) {
1069
1406
  if (!meta.a || !meta.ag)
1070
1407
  return;
1071
1408
  for (let g = 0; g < meta.ag.length; g++) {
1072
- const [targetPath, start, count] = meta.ag[g];
1073
- const el = resolve(targetPath);
1409
+ const [targetIndex, start, count] = meta.ag[g];
1410
+ const el = resolve(targetIndex);
1074
1411
  if (!el || el.nodeType !== 1)
1075
1412
  continue;
1076
1413
  for (let j = 0; j < count; j++) {
1077
1414
  const entry = meta.a[start + j];
1078
- if (entry)
1079
- instance.attrs.push(this.$makeAttr(el, entry, scope));
1415
+ if (!entry)
1416
+ continue;
1417
+ const binding = this.$makeAttr(el, entry, scope);
1418
+ instance.attrs.push(binding);
1419
+ if (primeSSRComplexProperties &&
1420
+ binding.kind === ATTR_KIND_COMPLEX &&
1421
+ this.$attrStateIsKnown(binding)) {
1422
+ this.$primeSSRComplexProperty(binding);
1423
+ }
1424
+ }
1425
+ }
1426
+ }
1427
+ $primeSSRComplexProperty(binding) {
1428
+ const element = binding.element;
1429
+ const value = this.$resolveValue(binding.path, binding.scope);
1430
+ this.$writeComplexProperty(element, binding.name, value, false);
1431
+ }
1432
+ $writeComplexProperty(element, name, value, replayAfterHydration) {
1433
+ const target = element;
1434
+ const setStateKey = target[WEBUI_SET_STATE_KEY];
1435
+ if (typeof setStateKey === 'function') {
1436
+ if (setStateKey.call(element, name, value)) {
1437
+ const flush = target['$flushUpdates'];
1438
+ if (typeof flush === 'function')
1439
+ flush.call(element);
1440
+ return;
1080
1441
  }
1442
+ target[name] = value;
1443
+ return;
1444
+ }
1445
+ if (isUnupgradedWebUITarget(element)) {
1446
+ queuePendingParentState(element, name, value, replayAfterHydration);
1447
+ return;
1081
1448
  }
1449
+ target[name] = value;
1082
1450
  }
1083
1451
  $finalize(_instance, _root, _meta, _resolver, _scope) { }
1084
1452
  $makeAttr(el, entry, scope) {
@@ -1178,29 +1546,65 @@ export class TemplateElement extends HTMLElement {
1178
1546
  }
1179
1547
  this.$pathIndex = index;
1180
1548
  }
1181
- $updateBindings(texts, attrs, conds, repeats) {
1549
+ $updateBindings(texts, attrs, conds, repeats, requireKnownState = false) {
1182
1550
  const gated = this.$hasUnknownScopes;
1183
1551
  for (let i = 0; i < texts.length; i++) {
1184
1552
  const binding = texts[i];
1185
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1553
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1554
+ (!requireKnownState || this.$textStateIsKnown(binding))) {
1186
1555
  this.$patchText(binding);
1556
+ }
1187
1557
  }
1188
1558
  for (let i = 0; i < attrs.length; i++) {
1189
1559
  const binding = attrs[i];
1190
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1560
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1561
+ (!requireKnownState || this.$attrStateIsKnown(binding))) {
1191
1562
  this.$patchAttr(binding);
1563
+ }
1192
1564
  }
1193
1565
  for (let i = 0; i < conds.length; i++) {
1194
1566
  const binding = conds[i];
1195
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1567
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1568
+ (!requireKnownState || this.$pathsAreKnown(binding.condition[1], binding.scope))) {
1196
1569
  this.$toggleCond(binding);
1570
+ }
1197
1571
  }
1198
1572
  for (let i = 0; i < repeats.length; i++) {
1199
1573
  const binding = repeats[i];
1200
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1574
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1575
+ (!requireKnownState || this.$hasStateRoot(binding.collection, binding.scope))) {
1201
1576
  syncRepeat(this, binding);
1577
+ }
1202
1578
  }
1203
1579
  }
1580
+ $textStateIsKnown(binding) {
1581
+ if (binding.parts)
1582
+ return this.$partsAreKnown(binding.parts, binding.scope);
1583
+ return !binding.path || this.$hasStateRoot(binding.path, binding.scope);
1584
+ }
1585
+ $attrStateIsKnown(binding) {
1586
+ if (binding.path && !this.$hasStateRoot(binding.path, binding.scope))
1587
+ return false;
1588
+ if (binding.parts && !this.$partsAreKnown(binding.parts, binding.scope))
1589
+ return false;
1590
+ return !binding.condition
1591
+ || this.$pathsAreKnown(binding.condition[1], binding.scope);
1592
+ }
1593
+ $partsAreKnown(parts, scope) {
1594
+ for (let i = 0; i < parts.length; i++) {
1595
+ const part = parts[i];
1596
+ if (typeof part !== 'string' && !this.$hasStateRoot(part[0], scope))
1597
+ return false;
1598
+ }
1599
+ return true;
1600
+ }
1601
+ $pathsAreKnown(paths, scope) {
1602
+ for (let i = 0; i < paths.length; i++) {
1603
+ if (!this.$hasStateRoot(paths[i], scope))
1604
+ return false;
1605
+ }
1606
+ return true;
1607
+ }
1204
1608
  $scopeIsKnown(scope) {
1205
1609
  let frame = scope;
1206
1610
  while (frame) {
@@ -1211,7 +1615,7 @@ export class TemplateElement extends HTMLElement {
1211
1615
  return true;
1212
1616
  }
1213
1617
  $updateInstance(instance) {
1214
- this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats);
1618
+ this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats, this.$guardUnknownState === true);
1215
1619
  }
1216
1620
  $patchText(b) {
1217
1621
  let val;
@@ -1239,17 +1643,7 @@ export class TemplateElement extends HTMLElement {
1239
1643
  switch (b.kind) {
1240
1644
  case ATTR_KIND_COMPLEX: {
1241
1645
  const v = this.$resolveValue(b.path, b.scope);
1242
- const target = el;
1243
- const setStateKey = target[WEBUI_SET_STATE_KEY];
1244
- if (typeof setStateKey === 'function') {
1245
- if (setStateKey.call(el, b.name, v)) {
1246
- const flush = target['$flushUpdates'];
1247
- if (typeof flush === 'function')
1248
- flush.call(el);
1249
- break;
1250
- }
1251
- }
1252
- target[b.name] = v;
1646
+ this.$writeComplexProperty(el, b.name, v, true);
1253
1647
  break;
1254
1648
  }
1255
1649
  case ATTR_KIND_BOOLEAN: {
@@ -1336,7 +1730,7 @@ export class TemplateElement extends HTMLElement {
1336
1730
  || (path.length > frame.name.length
1337
1731
  && path.charCodeAt(frame.name.length) === 46
1338
1732
  && path.startsWith(frame.name))) {
1339
- return frame.value !== undefined;
1733
+ return frame.known !== false;
1340
1734
  }
1341
1735
  frame = frame.parent;
1342
1736
  }