@microsoft/webui-framework 0.0.16 → 0.0.18

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.
@@ -1,23 +1,4 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT license.
3
- /**
4
- * Reactive decorators for WebUIElement properties.
5
- *
6
- * Uses TypeScript's `experimentalDecorators` emit, matching the FAST ecosystem
7
- * conventions.
8
- */
9
- // ---------------------------------------------------------------------------
10
- // Internal helpers
11
- // ---------------------------------------------------------------------------
12
- /**
13
- * Map of camelCase property names to their HTML attribute names.
14
- *
15
- * ARIA attributes (`ariaXxxYyy → aria-` + lowercase remainder) are handled
16
- * algorithmically in `toKebabCase`. Only HTML global/element attributes
17
- * with irregular mappings (concatenated lowercase) need explicit entries.
18
- */
19
1
  const propertyToAttribute = Object.assign(Object.create(null), {
20
- // --- HTML global/element attributes ---
21
2
  accessKey: 'accesskey',
22
3
  autoCapitalize: 'autocapitalize',
23
4
  contentEditable: 'contenteditable',
@@ -40,41 +21,11 @@ const propertyToAttribute = Object.assign(Object.create(null), {
40
21
  tabIndex: 'tabindex',
41
22
  useMap: 'usemap',
42
23
  });
43
- /**
44
- * Convert a camelCase DOM property name into its kebab-case HTML attribute form.
45
- *
46
- * This function is optimized for framework-level hot paths where attribute
47
- * normalization may run thousands of times per render. It performs three
48
- * progressively cheaper checks:
49
- *
50
- * 1. **Direct lookup for irregular mappings**
51
- * Many DOM properties (e.g., `readOnly`, `tabIndex`, `crossOrigin`) do not
52
- * follow simple camelCase → kebab-case rules. These are resolved through a
53
- * precomputed `propertyToAttribute` map for O(1) returns with no string
54
- * processing.
55
- *
56
- * 2. **Fast path for ARIA attributes**
57
- * ARIA properties always begin with `aria` followed by an uppercase letter
58
- * (e.g., `ariaDescribedBy`). These map to `aria-` + the lowercase remainder.
59
- * This branch avoids the general loop and uses the engine-optimized
60
- * `.toLowerCase()` for the suffix.
61
- *
62
- * 3. **General camelCase → kebab-case conversion**
63
- * For all other inputs, the function performs a tight ASCII-only scan:
64
- * uppercase A–Z (65–90) are converted to lowercase and prefixed with `-`,
65
- * while all other characters are copied as-is. This avoids regex engines,
66
- * callback allocations, and match objects, producing predictable,
67
- * allocation-minimal performance ideal for DOM attribute reflection.
68
- *
69
- * The result is a predictable, JIT-friendly transformation suitable for
70
- * attribute diffing, SSR serialization, and runtime DOM patching.
71
- */
72
24
  export function toKebabCase(str) {
73
25
  const mapped = propertyToAttribute[str];
74
26
  if (mapped)
75
27
  return mapped;
76
- // ARIA properties: ariaXxxYyy → aria- + lowercase remainder
77
- if (str.length > 4 && str.charCodeAt(0) === 97 /* a */ && str.startsWith('aria') && str.charCodeAt(4) >= 65 && str.charCodeAt(4) <= 90) {
28
+ if (str.length > 4 && str.charCodeAt(0) === 97 && str.startsWith('aria') && str.charCodeAt(4) >= 65 && str.charCodeAt(4) <= 90) {
78
29
  return 'aria-' + str.slice(4).toLowerCase();
79
30
  }
80
31
  let out = '';
@@ -85,96 +36,12 @@ export function toKebabCase(str) {
85
36
  return out;
86
37
  }
87
38
  const reflectingAttribute = Symbol('webui.reflectingAttribute');
39
+ const EMPTY_SET = Object.freeze(new Set());
88
40
  function parentConstructor(ctor) {
89
41
  const parent = Object.getPrototypeOf(ctor);
90
42
  return typeof parent === 'function' && parent !== Function.prototype ? parent : null;
91
43
  }
92
- function createReactiveProperty(proto, name, attrDefinition) {
93
- const backingKey = `_${name}`;
94
- const changedKey = `${name}Changed`;
95
- Object.defineProperty(proto, name, {
96
- get() {
97
- return this[backingKey];
98
- },
99
- set(newValue) {
100
- const oldValue = this[backingKey];
101
- if (Object.is(oldValue, newValue))
102
- return;
103
- this[backingKey] = newValue;
104
- if (attrDefinition && this['$ready'] === true) {
105
- reflectPropertyToAttribute(this, attrDefinition, newValue);
106
- }
107
- const cb = this[changedKey];
108
- if (typeof cb === 'function') {
109
- cb.call(this, oldValue, newValue);
110
- }
111
- if (this.isConnected) {
112
- const upd = this['$update'];
113
- if (upd)
114
- upd.call(this, name);
115
- }
116
- },
117
- enumerable: true,
118
- configurable: true,
119
- });
120
- }
121
- function reflectPropertyToAttribute(instance, definition, value) {
122
- const element = instance;
123
- const attrName = definition.attribute;
124
- if (definition.boolean) {
125
- const shouldHaveAttribute = Boolean(value);
126
- if (element.hasAttribute(attrName) === shouldHaveAttribute)
127
- return;
128
- setReflectingAttribute(instance, attrName);
129
- try {
130
- if (shouldHaveAttribute)
131
- element.setAttribute(attrName, '');
132
- else
133
- element.removeAttribute(attrName);
134
- }
135
- finally {
136
- restoreReflectingAttribute(instance);
137
- }
138
- return;
139
- }
140
- if (value == null) {
141
- if (!element.hasAttribute(attrName))
142
- return;
143
- setReflectingAttribute(instance, attrName);
144
- try {
145
- element.removeAttribute(attrName);
146
- }
147
- finally {
148
- restoreReflectingAttribute(instance);
149
- }
150
- return;
151
- }
152
- const attrValue = typeof value === 'string' ? value : String(value);
153
- if (element.getAttribute(attrName) === attrValue)
154
- return;
155
- setReflectingAttribute(instance, attrName);
156
- try {
157
- element.setAttribute(attrName, attrValue);
158
- }
159
- finally {
160
- restoreReflectingAttribute(instance);
161
- }
162
- }
163
- function setReflectingAttribute(instance, attrName) {
164
- instance[reflectingAttribute] = attrName;
165
- }
166
- function restoreReflectingAttribute(instance) {
167
- instance[reflectingAttribute] = undefined;
168
- }
169
- // ---------------------------------------------------------------------------
170
- // @observable
171
- // ---------------------------------------------------------------------------
172
- /** Per-class registry of @observable property names. */
173
44
  const observableRegistry = new WeakMap();
174
- /**
175
- * Get the set of @observable property names registered for a class.
176
- */
177
- const EMPTY_SET = Object.freeze(new Set());
178
45
  export function getObservableNames(ctor) {
179
46
  const names = observableRegistry.get(ctor);
180
47
  if (names)
@@ -192,26 +59,7 @@ function registerObservableProperty(ctor, name) {
192
59
  }
193
60
  names.add(name);
194
61
  }
195
- /**
196
- * Marks a property as observable. When the value changes the decorator will:
197
- * 1. Call `this.<prop>Changed(oldValue, newValue)` if defined.
198
- * 2. Call `this.$update(name)` if the element is connected, targeting
199
- * only bindings that reference this property.
200
- */
201
- export function observable(target, name) {
202
- const ctor = target.constructor;
203
- registerObservableProperty(ctor, name);
204
- createReactiveProperty(target, name);
205
- }
206
- // ---------------------------------------------------------------------------
207
- // @attr
208
- // ---------------------------------------------------------------------------
209
- /**
210
- * Registry of attribute-name → property-name mappings per constructor.
211
- * Used by `attributeChangedCallback` to route attribute changes to properties.
212
- */
213
62
  const attrByAttribute = new WeakMap();
214
- /** Registry of property-name → attribute metadata, used for mount-time sync. */
215
63
  const attrByProperty = new WeakMap();
216
64
  function inheritedAttrMap(registry, ctor) {
217
65
  let current = parentConstructor(ctor);
@@ -246,6 +94,97 @@ function attrPropertyMapFor(ctor) {
246
94
  export function isAttributeProperty(ctor, property) {
247
95
  return attrPropertyMapFor(ctor)?.has(property) === true;
248
96
  }
97
+ function setReflectingAttribute(instance, attrName) {
98
+ instance[reflectingAttribute] = attrName;
99
+ }
100
+ function restoreReflectingAttribute(instance) {
101
+ instance[reflectingAttribute] = undefined;
102
+ }
103
+ function reflectPropertyToAttribute(instance, definition, value) {
104
+ const element = instance;
105
+ const attrName = definition.attribute;
106
+ if (definition.boolean) {
107
+ const shouldHaveAttribute = Boolean(value);
108
+ if (element.hasAttribute(attrName) === shouldHaveAttribute)
109
+ return;
110
+ setReflectingAttribute(instance, attrName);
111
+ try {
112
+ if (shouldHaveAttribute)
113
+ element.setAttribute(attrName, '');
114
+ else
115
+ element.removeAttribute(attrName);
116
+ }
117
+ finally {
118
+ restoreReflectingAttribute(instance);
119
+ }
120
+ return;
121
+ }
122
+ if (value == null) {
123
+ if (!element.hasAttribute(attrName))
124
+ return;
125
+ setReflectingAttribute(instance, attrName);
126
+ try {
127
+ element.removeAttribute(attrName);
128
+ }
129
+ finally {
130
+ restoreReflectingAttribute(instance);
131
+ }
132
+ return;
133
+ }
134
+ const attrValue = typeof value === 'string' ? value : String(value);
135
+ if (element.getAttribute(attrName) === attrValue)
136
+ return;
137
+ setReflectingAttribute(instance, attrName);
138
+ try {
139
+ element.setAttribute(attrName, attrValue);
140
+ }
141
+ finally {
142
+ restoreReflectingAttribute(instance);
143
+ }
144
+ }
145
+ export function syncAttrProperties(instance, ctor) {
146
+ const attrs = attrPropertyMapFor(ctor);
147
+ if (!attrs)
148
+ return;
149
+ const reactiveInstance = instance;
150
+ for (const definition of attrs.values()) {
151
+ reflectPropertyToAttribute(reactiveInstance, definition, reactiveInstance[definition.property]);
152
+ }
153
+ }
154
+ function createReactiveProperty(proto, name, attrDefinition) {
155
+ const backingKey = `_${name}`;
156
+ const changedKey = `${name}Changed`;
157
+ Object.defineProperty(proto, name, {
158
+ get() {
159
+ return this[backingKey];
160
+ },
161
+ set(newValue) {
162
+ const oldValue = this[backingKey];
163
+ if (Object.is(oldValue, newValue))
164
+ return;
165
+ this[backingKey] = newValue;
166
+ if (attrDefinition && this['$ready'] === true) {
167
+ reflectPropertyToAttribute(this, attrDefinition, newValue);
168
+ }
169
+ const cb = this[changedKey];
170
+ if (typeof cb === 'function') {
171
+ cb.call(this, oldValue, newValue);
172
+ }
173
+ if (this.isConnected) {
174
+ const upd = this['$update'];
175
+ if (upd)
176
+ upd.call(this, name);
177
+ }
178
+ },
179
+ enumerable: true,
180
+ configurable: true,
181
+ });
182
+ }
183
+ export function observable(target, name) {
184
+ const ctor = target.constructor;
185
+ registerObservableProperty(ctor, name);
186
+ createReactiveProperty(target, name);
187
+ }
249
188
  function applyAttr(target, name, options) {
250
189
  const proto = target;
251
190
  const ctor = proto.constructor;
@@ -255,11 +194,8 @@ function applyAttr(target, name, options) {
255
194
  property: name,
256
195
  boolean: options?.mode === 'boolean',
257
196
  };
258
- // 1. Install the reactive getter/setter (same as @observable), with
259
- // attribute reflection enabled after the element finishes hydration.
260
197
  registerObservableProperty(ctor, name);
261
198
  createReactiveProperty(proto, name, definition);
262
- // 2. Register the attribute mapping.
263
199
  let byAttribute = attrByAttribute.get(ctor);
264
200
  if (!byAttribute) {
265
201
  const inherited = inheritedAttrMap(attrByAttribute, ctor);
@@ -274,21 +210,17 @@ function applyAttr(target, name, options) {
274
210
  attrByProperty.set(ctor, byProperty);
275
211
  }
276
212
  byProperty.set(name, definition);
277
- // 3. Accumulate observed attributes on the constructor.
278
213
  if (!Object.prototype.hasOwnProperty.call(ctor, '_observedAttrs')) {
279
214
  const inheritedAttrs = ctor._observedAttrs;
280
215
  ctor._observedAttrs = inheritedAttrs ? inheritedAttrs.slice() : [];
281
- // Define the static getter that `customElements.define` inspects.
282
216
  Object.defineProperty(ctor, 'observedAttributes', {
283
217
  get() {
284
218
  return ctor._observedAttrs ?? [];
285
219
  },
286
220
  configurable: true,
287
221
  });
288
- // Patch `attributeChangedCallback` once per class.
289
222
  const origACB = proto['attributeChangedCallback'];
290
223
  proto['attributeChangedCallback'] = function (attribute, oldVal, newVal) {
291
- // Route the attribute change to the corresponding property.
292
224
  const definition = attrDefinitionFor(this.constructor, attribute);
293
225
  if (definition !== undefined &&
294
226
  this[reflectingAttribute] !== attribute) {
@@ -296,7 +228,6 @@ function applyAttr(target, name, options) {
296
228
  ? newVal !== null
297
229
  : newVal;
298
230
  }
299
- // Preserve any pre-existing attributeChangedCallback.
300
231
  if (origACB) {
301
232
  origACB.call(this, attribute, oldVal, newVal);
302
233
  }
@@ -304,15 +235,6 @@ function applyAttr(target, name, options) {
304
235
  }
305
236
  ctor._observedAttrs.push(attrName);
306
237
  }
307
- export function syncAttrProperties(instance, ctor) {
308
- const attrs = attrPropertyMapFor(ctor);
309
- if (!attrs)
310
- return;
311
- const reactiveInstance = instance;
312
- for (const definition of attrs.values()) {
313
- reflectPropertyToAttribute(reactiveInstance, definition, reactiveInstance[definition.property]);
314
- }
315
- }
316
238
  export function attr(targetOrOptions, name) {
317
239
  if (typeof name === 'string') {
318
240
  applyAttr(targetOrOptions, name);
@@ -1,23 +1,3 @@
1
- /**
2
- * Keyed child reconciliation for `@for(item of items)` repeat blocks.
3
- *
4
- * Diff that matches old instances by key, reuses what it
5
- * can, creates/removes the rest, then reorders DOM nodes in one forward pass.
6
- */
7
1
  import type { RepeatBinding, RepeatHost } from './types.js';
8
- /** Resolve a dotted path from a start offset without allocating. */
9
2
  export declare function dotWalk(cursor: unknown, path: string, from: number): unknown;
10
- /**
11
- * Resolve a dotted path against a repeat scope variable.
12
- *
13
- * When a binding inside `@for(item of items)` references `item.title`,
14
- * this function looks up `title` on the current scope value.
15
- */
16
- export declare function resolveRepeatValue(scopeVar: string, scope: unknown, path: string): unknown;
17
- /**
18
- * Reconcile a repeat binding against its current collection value.
19
- *
20
- * Called by `$updateInstance` on every reactive update. Resolves the
21
- * collection path, diffs old vs. new items by key, and patches the DOM.
22
- */
23
3
  export declare function syncRepeat(host: RepeatHost, rep: RepeatBinding): void;
@@ -1,16 +1,12 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT license.
3
- // ── Helpers ─────────────────────────────────────────────────────────
4
1
  function asParent(node) {
5
2
  if (!node)
6
3
  return null;
7
4
  return 'childNodes' in node ? node : null;
8
5
  }
9
- /** Resolve a dotted path from a start offset without allocating. */
10
6
  export function dotWalk(cursor, path, from) {
11
7
  let start = from;
12
8
  for (let i = from; i <= path.length; i++) {
13
- if (i === path.length || path.charCodeAt(i) === 46 /* . */) {
9
+ if (i === path.length || path.charCodeAt(i) === 46) {
14
10
  if (cursor == null || typeof cursor !== 'object')
15
11
  return undefined;
16
12
  cursor = cursor[path.slice(start, i)];
@@ -19,53 +15,27 @@ export function dotWalk(cursor, path, from) {
19
15
  }
20
16
  return cursor;
21
17
  }
22
- /**
23
- * Resolve a dotted path against a repeat scope variable.
24
- *
25
- * When a binding inside `@for(item of items)` references `item.title`,
26
- * this function looks up `title` on the current scope value.
27
- */
28
- export function resolveRepeatValue(scopeVar, scope, path) {
29
- if (path === scopeVar)
30
- return scope;
31
- if (path.length <= scopeVar.length || path.charCodeAt(scopeVar.length) !== 46 /* '.' */ || !path.startsWith(scopeVar))
32
- return undefined;
33
- return dotWalk(scope, path, scopeVar.length + 1);
34
- }
35
- /** Compute a key for an item using the cached key path, or null. */
36
18
  function itemKey(item, keyPath) {
37
19
  if (keyPath === undefined || keyPath === '')
38
20
  return null;
39
21
  const v = dotWalk(item, keyPath, 0);
40
22
  return v != null ? String(v) : '';
41
23
  }
42
- /** Build a scope frame for a repeat item. */
43
24
  function itemScope(rep, item) {
44
25
  return { name: rep.itemVar, value: item, parent: rep.scope };
45
26
  }
46
- // ── Reconciliation ──────────────────────────────────────────────────
47
- /**
48
- * Reconcile a repeat binding against its current collection value.
49
- *
50
- * Called by `$updateInstance` on every reactive update. Resolves the
51
- * collection path, diffs old vs. new items by key, and patches the DOM.
52
- */
53
27
  export function syncRepeat(host, rep) {
54
28
  const resolved = host.$resolveValue(rep.collection, rep.scope);
55
29
  const items = Array.isArray(resolved) ? resolved : [];
56
- // Locate the container once and cache it.
57
30
  let container = rep.container
58
31
  ?? (rep.start ? asParent(rep.start.parentNode) : null)
59
32
  ?? (rep.owner.nodes[0] ? asParent(rep.owner.nodes[0].parentNode) : null);
60
33
  if (!container)
61
34
  return;
62
35
  rep.container = container;
63
- // Before the first client-side sync, bail if the collection hasn't
64
- // been explicitly set but SSR children already exist.
65
36
  if (!rep.synced && items.length === 0 && rep.instances.length > 0)
66
37
  return;
67
38
  rep.synced = true;
68
- // If there are no items, just tear down everything.
69
39
  if (items.length === 0) {
70
40
  for (let i = 0; i < rep.instances.length; i += 1) {
71
41
  host.$removeInstance(rep.instances[i].instance);
@@ -73,45 +43,40 @@ export function syncRepeat(host, rep) {
73
43
  rep.instances = [];
74
44
  return;
75
45
  }
76
- const keyPath = Object.values(rep.attrMap)[0];
46
+ const keyPath = rep.keyPath;
77
47
  const hasKeys = keyPath !== undefined && keyPath !== '';
78
48
  const oldInstances = rep.instances;
79
- // ── Fast path for unkeyed (index-based) repeats ────────────────
80
49
  if (!hasKeys) {
81
- const next = [];
82
- const reuseCount = Math.min(oldInstances.length, items.length);
83
- // Reuse existing instances by index
50
+ const oldLength = oldInstances.length;
51
+ const reuseCount = Math.min(oldLength, items.length);
52
+ let nextCount = reuseCount;
84
53
  for (let i = 0; i < reuseCount; i += 1) {
85
54
  const entry = oldInstances[i];
86
55
  entry.value = items[i];
87
56
  if (entry.instance.scope)
88
57
  entry.instance.scope.value = items[i];
89
- next.push(entry);
90
58
  }
91
- // Create new instances for items beyond old length
92
59
  for (let i = reuseCount; i < items.length; i += 1) {
93
60
  const scope = itemScope(rep, items[i]);
94
61
  const instance = host.$createBlockInstance(rep.blockIndex, scope);
95
62
  if (instance) {
96
- next.push({ key: null, value: items[i], instance });
63
+ oldInstances[nextCount] = { key: null, value: items[i], instance };
64
+ nextCount += 1;
97
65
  }
98
66
  }
99
- // Remove excess old instances
100
- for (let i = reuseCount; i < oldInstances.length; i += 1) {
67
+ for (let i = reuseCount; i < oldLength; i += 1) {
101
68
  host.$removeInstance(oldInstances[i].instance);
102
69
  }
103
- rep.instances = next;
70
+ oldInstances.length = nextCount;
104
71
  let cursor = rep.start;
105
- for (let i = 0; i < next.length; i += 1) {
106
- cursor = host.$insertInstanceAfter(cursor, container, next[i].instance);
72
+ for (let i = 0; i < oldInstances.length; i += 1) {
73
+ cursor = host.$insertInstanceAfter(cursor, container, oldInstances[i].instance);
107
74
  }
108
75
  for (let i = 0; i < reuseCount; i += 1) {
109
- host.$updateInstance(next[i].instance);
76
+ host.$updateInstance(oldInstances[i].instance);
110
77
  }
111
78
  return;
112
79
  }
113
- // ── Keyed diff ─────────────────────────────────────────────────
114
- // ── Build old-key → instance map ────────────────────────────────
115
80
  const oldByKey = new Map();
116
81
  for (let i = 0; i < oldInstances.length; i += 1) {
117
82
  const entry = oldInstances[i];
@@ -119,44 +84,42 @@ export function syncRepeat(host, rep) {
119
84
  if (k != null)
120
85
  oldByKey.set(k, entry);
121
86
  }
122
- // ── Match / create ──────────────────────────────────────────────
123
- const next = [];
87
+ let nextCount = 0;
124
88
  for (let i = 0; i < items.length; i += 1) {
125
89
  const item = items[i];
126
90
  const key = itemKey(item, keyPath);
127
91
  const existing = key != null ? oldByKey.get(key) : undefined;
128
92
  if (existing) {
129
- oldByKey.delete(key);
93
+ oldByKey.set(key, undefined);
130
94
  existing.value = item;
131
95
  existing.key = key;
132
96
  if (existing.instance.scope)
133
97
  existing.instance.scope.value = item;
134
- next.push(existing);
98
+ oldInstances[nextCount] = existing;
99
+ nextCount += 1;
135
100
  }
136
101
  else {
137
102
  const scope = itemScope(rep, item);
138
103
  const instance = host.$createBlockInstance(rep.blockIndex, scope);
139
104
  if (instance) {
140
- next.push({ key: key ?? null, value: item, instance });
105
+ oldInstances[nextCount] = { key: key ?? null, value: item, instance };
106
+ nextCount += 1;
141
107
  }
142
108
  }
143
109
  }
144
- // ── Remove unmatched old instances ──────────────────────────────
145
110
  for (const leftover of oldByKey.values()) {
146
- host.$removeInstance(leftover.instance);
111
+ if (leftover)
112
+ host.$removeInstance(leftover.instance);
147
113
  }
148
- rep.instances = next;
149
- // ── Reorder DOM (forward pass) ──────────────────────────────────
150
- // Newly-created instances were patched while detached. Reused instances
151
- // update after moving so nested structural nodes stay with the item.
114
+ oldInstances.length = nextCount;
152
115
  let cursor = rep.start;
153
- for (let i = 0; i < next.length; i += 1) {
154
- cursor = host.$insertInstanceAfter(cursor, container, next[i].instance);
116
+ for (let i = 0; i < oldInstances.length; i += 1) {
117
+ cursor = host.$insertInstanceAfter(cursor, container, oldInstances[i].instance);
155
118
  }
156
119
  for (let i = 0; i < oldInstances.length; i += 1) {
157
120
  const entry = oldInstances[i];
158
- const k = entry.key;
159
- if (k != null && !oldByKey.has(k))
121
+ if (entry.key != null && oldByKey.has(entry.key) && oldByKey.get(entry.key) === undefined) {
160
122
  host.$updateInstance(entry.instance);
123
+ }
161
124
  }
162
125
  }
@@ -1,54 +1,10 @@
1
- /**
2
- * Hydration marker utilities for zero-DOM-mutation in-place hydration.
3
- *
4
- * The WebUI handler plugin emits lightweight HTML comment markers around
5
- * structural boundaries (for-loops and if-conditions). These utilities
6
- * walk markers and locate elements so the hydration path can wire
7
- * bindings in-place without reparenting DOM nodes.
8
- *
9
- * Marker format:
10
- * <!--wr--> repeat block start
11
- * <!--/wr--> repeat block end
12
- * <!--wi--> repeat item boundary
13
- * <!--wc--> conditional block start
14
- * <!--/wc--> conditional block end
15
- */
16
1
  export declare const MARKER_REPEAT_START = "wr";
17
2
  export declare const MARKER_REPEAT_END = "/wr";
18
3
  export declare const MARKER_COND_START = "wc";
19
4
  export declare const MARKER_COND_END = "/wc";
20
- /**
21
- * Collect the item markers (<!--wi-->) within a repeat range.
22
- *
23
- * Walks siblings from the repeat start marker to the repeat end marker.
24
- * Returns an array of <!--wi--> comment nodes that delineate items.
25
- */
26
5
  export declare function collectItemMarkers(repeatStart: Comment): {
27
6
  items: Comment[];
28
7
  end: Comment | null;
29
8
  };
30
- /**
31
- * Get the next element sibling after a marker comment, skipping
32
- * whitespace text nodes and other comments.
33
- */
34
9
  export declare function nextElement(marker: Comment): Element | null;
35
- /**
36
- * Find the Nth child of a given nodeType, skipping structural block ranges.
37
- *
38
- * The compiled template static HTML (`meta.h`) does not contain conditional
39
- * or repeat block content — those are stored as separate block metadata.
40
- * But the SSR DOM has this content rendered inline between marker pairs
41
- * (`<!--wc-->...<!--/wc-->` and `<!--wr-->...<!--/wr-->`).
42
- *
43
- * This function walks `parent.firstChild` → siblings, counting only
44
- * children of the requested `nodeType` that are NOT inside a structural
45
- * block range. Nested blocks of the same type are handled via depth
46
- * tracking. Returns the child at the given `ordinal`, or null.
47
- *
48
- * Used by `$resolveSSR` (element ordinals) and `$findSSRText` (text
49
- * ordinals) to keep SSR DOM ordinals aligned with template metadata.
50
- *
51
- * **Requires closing markers to still be in the DOM** — caller must
52
- * not remove `<!--/wc-->` or `<!--/wr-->` before all resolution is done.
53
- */
54
10
  export declare function findByOrdinal(parent: Node, nodeType: number, ordinal: number): Node | null;