@nisli/core 0.56.0 → 0.57.1

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.
Files changed (114) hide show
  1. package/README.md +70 -9
  2. package/dist/bundler/component.js +409 -0
  3. package/dist/bundler/component.js.map +7 -0
  4. package/dist/bundler/context.js +27 -0
  5. package/dist/bundler/context.js.map +7 -0
  6. package/dist/bundler/diagnostics.js +55 -0
  7. package/dist/bundler/diagnostics.js.map +7 -0
  8. package/dist/bundler/disposable.js +9 -0
  9. package/dist/bundler/disposable.js.map +7 -0
  10. package/dist/bundler/element-context.js +47 -0
  11. package/dist/bundler/element-context.js.map +7 -0
  12. package/dist/bundler/emitter.js +70 -0
  13. package/dist/bundler/emitter.js.map +7 -0
  14. package/dist/bundler/hmr/registry.js +72 -0
  15. package/dist/bundler/hmr/registry.js.map +7 -0
  16. package/dist/bundler/hmr/transform.js +30 -0
  17. package/dist/bundler/hmr/transform.js.map +7 -0
  18. package/dist/bundler/index.js +103 -0
  19. package/dist/bundler/index.js.map +7 -0
  20. package/dist/bundler/injector.js +75 -0
  21. package/dist/bundler/injector.js.map +7 -0
  22. package/dist/bundler/lifecycle.js +56 -0
  23. package/dist/bundler/lifecycle.js.map +7 -0
  24. package/dist/bundler/projection.js +92 -0
  25. package/dist/bundler/projection.js.map +7 -0
  26. package/dist/bundler/query.js +302 -0
  27. package/dist/bundler/query.js.map +7 -0
  28. package/dist/bundler/ref.js +16 -0
  29. package/dist/bundler/ref.js.map +7 -0
  30. package/dist/bundler/resource.js +99 -0
  31. package/dist/bundler/resource.js.map +7 -0
  32. package/dist/bundler/settle.js +43 -0
  33. package/dist/bundler/settle.js.map +7 -0
  34. package/dist/bundler/signal.js +403 -0
  35. package/dist/bundler/signal.js.map +7 -0
  36. package/dist/bundler/template-audit.js +162 -0
  37. package/dist/bundler/template-audit.js.map +7 -0
  38. package/dist/bundler/template.js +922 -0
  39. package/dist/bundler/template.js.map +7 -0
  40. package/dist/bundler/view-transition.js +20 -0
  41. package/dist/bundler/view-transition.js.map +7 -0
  42. package/dist/bundler/vite-hmr/plugin.js +32 -0
  43. package/dist/bundler/vite-hmr/plugin.js.map +7 -0
  44. package/dist/bundler/vite-hmr/runtime.js +8 -0
  45. package/dist/bundler/vite-hmr/runtime.js.map +7 -0
  46. package/dist/index.js +2682 -47
  47. package/dist/index.js.map +7 -1
  48. package/dist/index.production.js +2 -0
  49. package/dist/index.production.js.map +6 -0
  50. package/dist/{diagnostics.d.ts → types/diagnostics.d.ts} +8 -15
  51. package/dist/{template-audit.d.ts → types/template-audit.d.ts} +4 -5
  52. package/dist/{template.d.ts → types/template.d.ts} +2 -1
  53. package/package.json +15 -10
  54. package/dist/component.js +0 -633
  55. package/dist/component.js.map +0 -1
  56. package/dist/context.js +0 -55
  57. package/dist/context.js.map +0 -1
  58. package/dist/diagnostics.js +0 -118
  59. package/dist/diagnostics.js.map +0 -1
  60. package/dist/disposable.js +0 -8
  61. package/dist/disposable.js.map +0 -1
  62. package/dist/element-context.js +0 -108
  63. package/dist/element-context.js.map +0 -1
  64. package/dist/emitter.js +0 -91
  65. package/dist/emitter.js.map +0 -1
  66. package/dist/hmr/registry.js +0 -136
  67. package/dist/hmr/registry.js.map +0 -1
  68. package/dist/hmr/transform.js +0 -52
  69. package/dist/hmr/transform.js.map +0 -1
  70. package/dist/injector.js +0 -118
  71. package/dist/injector.js.map +0 -1
  72. package/dist/lifecycle.js +0 -117
  73. package/dist/lifecycle.js.map +0 -1
  74. package/dist/projection.js +0 -179
  75. package/dist/projection.js.map +0 -1
  76. package/dist/query.js +0 -429
  77. package/dist/query.js.map +0 -1
  78. package/dist/ref.js +0 -43
  79. package/dist/ref.js.map +0 -1
  80. package/dist/resource.js +0 -129
  81. package/dist/resource.js.map +0 -1
  82. package/dist/settle.js +0 -106
  83. package/dist/settle.js.map +0 -1
  84. package/dist/signal.js +0 -645
  85. package/dist/signal.js.map +0 -1
  86. package/dist/template-audit.js +0 -246
  87. package/dist/template-audit.js.map +0 -1
  88. package/dist/template.js +0 -1244
  89. package/dist/template.js.map +0 -1
  90. package/dist/view-transition.js +0 -98
  91. package/dist/view-transition.js.map +0 -1
  92. package/dist/vite-hmr/plugin.js +0 -62
  93. package/dist/vite-hmr/plugin.js.map +0 -1
  94. package/dist/vite-hmr/runtime.js +0 -17
  95. package/dist/vite-hmr/runtime.js.map +0 -1
  96. /package/dist/{component.d.ts → types/component.d.ts} +0 -0
  97. /package/dist/{context.d.ts → types/context.d.ts} +0 -0
  98. /package/dist/{disposable.d.ts → types/disposable.d.ts} +0 -0
  99. /package/dist/{element-context.d.ts → types/element-context.d.ts} +0 -0
  100. /package/dist/{emitter.d.ts → types/emitter.d.ts} +0 -0
  101. /package/dist/{hmr → types/hmr}/registry.d.ts +0 -0
  102. /package/dist/{hmr → types/hmr}/transform.d.ts +0 -0
  103. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  104. /package/dist/{injector.d.ts → types/injector.d.ts} +0 -0
  105. /package/dist/{lifecycle.d.ts → types/lifecycle.d.ts} +0 -0
  106. /package/dist/{projection.d.ts → types/projection.d.ts} +0 -0
  107. /package/dist/{query.d.ts → types/query.d.ts} +0 -0
  108. /package/dist/{ref.d.ts → types/ref.d.ts} +0 -0
  109. /package/dist/{resource.d.ts → types/resource.d.ts} +0 -0
  110. /package/dist/{settle.d.ts → types/settle.d.ts} +0 -0
  111. /package/dist/{signal.d.ts → types/signal.d.ts} +0 -0
  112. /package/dist/{view-transition.d.ts → types/view-transition.d.ts} +0 -0
  113. /package/dist/{vite-hmr → types/vite-hmr}/plugin.d.ts +0 -0
  114. /package/dist/{vite-hmr → types/vite-hmr}/runtime.d.ts +0 -0
@@ -0,0 +1,922 @@
1
+ import {
2
+ signal,
3
+ isSignal,
4
+ effect,
5
+ computed,
6
+ untrack
7
+ } from "./signal.js";
8
+ import { isRef } from "./ref.js";
9
+ import { auditTemplate, captureCallsite } from "./template-audit.js";
10
+ const canMoveWithin = (parent) => typeof parent.moveBefore === "function";
11
+ function untracked(fn) {
12
+ let result;
13
+ untrack(() => {
14
+ result = fn();
15
+ });
16
+ return result;
17
+ }
18
+ function raw(value) {
19
+ return { __raw: true, value };
20
+ }
21
+ function isRawHtml(value) {
22
+ return typeof value === "object" && value !== null && "__raw" in value && value.__raw === true && typeof value.value === "string";
23
+ }
24
+ function sanitized(value) {
25
+ return { __sanitize: true, value };
26
+ }
27
+ function isSanitizedHtml(value) {
28
+ return typeof value === "object" && value !== null && "__sanitize" in value && value.__sanitize === true && typeof value.value === "string";
29
+ }
30
+ let sanitizerFallback = null;
31
+ function setSanitizerFallback(fn) {
32
+ sanitizerFallback = fn;
33
+ }
34
+ const nativeSetHtml = (el2, html2) => {
35
+ el2.setHTML(html2);
36
+ };
37
+ function sanitizerFor(el2) {
38
+ const platform = typeof Element === "function" ? Element.prototype.setHTML : void 0;
39
+ return typeof platform === "function" && el2.setHTML === platform ? nativeSetHtml : sanitizerFallback;
40
+ }
41
+ function isTrustedHtml(value) {
42
+ const tt = globalThis.trustedTypes;
43
+ return tt !== void 0 && tt.isHTML(value);
44
+ }
45
+ function nisliError(code, message) {
46
+ return new Error(`[nisli] ${code}: ${message}`);
47
+ }
48
+ const templateCache = /* @__PURE__ */ new WeakMap();
49
+ let parseCount = 0;
50
+ function __templateParseCount() {
51
+ return parseCount;
52
+ }
53
+ function parseTemplate(strings, valueCount) {
54
+ let template = templateCache.get(strings);
55
+ if (template) return template;
56
+ let htmlStr = "";
57
+ let inTag = false;
58
+ let quoteChar = null;
59
+ for (let i = 0; i < strings.length; i++) {
60
+ const s = strings[i] ?? "";
61
+ for (let c = 0; c < s.length; c++) {
62
+ const ch = s.charAt(c);
63
+ if (quoteChar) {
64
+ if (ch === quoteChar) quoteChar = null;
65
+ } else if (inTag) {
66
+ if (ch === ">") inTag = false;
67
+ else if (ch === '"' || ch === "'") quoteChar = ch;
68
+ } else {
69
+ if (ch === "<") inTag = true;
70
+ }
71
+ }
72
+ htmlStr += s;
73
+ if (i < valueCount) {
74
+ const needsQuotes = inTag && !quoteChar && /=\s*$/.test(s);
75
+ htmlStr += needsQuotes ? `"${createMarker(i)}"` : createMarker(i);
76
+ }
77
+ }
78
+ parseCount++;
79
+ template = document.createElement("template");
80
+ template.innerHTML = htmlStr;
81
+ templateCache.set(strings, template);
82
+ if (process.env.NODE_ENV !== "production") auditTemplate(strings, template);
83
+ return template;
84
+ }
85
+ const MARKER_PREFIX = "<!--bk-";
86
+ const MARKER_SUFFIX = "-->";
87
+ const ATTR_MARKER = "bk-";
88
+ function createMarker(index) {
89
+ return `${MARKER_PREFIX}${index}${MARKER_SUFFIX}`;
90
+ }
91
+ function html(strings, ...values) {
92
+ const bindings = [];
93
+ const disposers = [];
94
+ if (process.env.NODE_ENV !== "production") captureCallsite(strings);
95
+ let mounted = false;
96
+ return {
97
+ __templateResult: true,
98
+ mount(host) {
99
+ if (mounted && values.length > 0) {
100
+ throw nisliError(
101
+ "N105",
102
+ `mount() on an already-mounted TemplateResult with ${values.length} value binding(s) \u2014 dispose() it first or create a fresh template per mount. Template starts: ${JSON.stringify((strings[0] ?? "").slice(0, 60))}`
103
+ );
104
+ }
105
+ mounted = true;
106
+ const template = parseTemplate(strings, values.length);
107
+ const fragment = template.content.cloneNode(true);
108
+ const afterMount = [];
109
+ processNode(fragment, values, bindings, disposers, afterMount);
110
+ host.appendChild(fragment);
111
+ for (const sync of afterMount) sync();
112
+ },
113
+ dispose() {
114
+ for (const d of disposers) {
115
+ try {
116
+ d();
117
+ } catch (_) {
118
+ }
119
+ }
120
+ disposers.length = 0;
121
+ for (const b of bindings) {
122
+ if ("dispose" in b && b.dispose) {
123
+ try {
124
+ b.dispose();
125
+ } catch (_) {
126
+ }
127
+ }
128
+ }
129
+ bindings.length = 0;
130
+ mounted = false;
131
+ }
132
+ };
133
+ }
134
+ function processNode(node, values, bindings, disposers, afterMount) {
135
+ let afterChildren;
136
+ if (node.nodeType === Node.ELEMENT_NODE) {
137
+ const el2 = node;
138
+ afterChildren = processAttributes(el2, values, bindings, disposers, afterMount);
139
+ }
140
+ if (node.nodeType === Node.COMMENT_NODE) {
141
+ const comment = node;
142
+ const text = comment.textContent || "";
143
+ if (text.startsWith("bk-") && !isNaN(Number(text.slice(3)))) {
144
+ const index = Number(text.slice(3));
145
+ const value = values[index];
146
+ replaceMarkerWithBinding(comment, value, bindings, disposers);
147
+ return;
148
+ }
149
+ }
150
+ const children = [...node.childNodes];
151
+ for (const child of children) {
152
+ processNode(child, values, bindings, disposers, afterMount);
153
+ }
154
+ if (afterChildren) for (const bind of afterChildren) bind();
155
+ }
156
+ function processAttributes(el2, values, bindings, disposers, afterMount) {
157
+ const attrsToRemove = [];
158
+ let afterChildren;
159
+ for (const attr of [...el2.attributes]) {
160
+ const name = attr.name;
161
+ const attrValue = attr.value;
162
+ if (name.startsWith("@")) {
163
+ const eventParts = name.slice(1).split(".");
164
+ const eventName = eventParts[0] ?? "";
165
+ const modifiers = eventParts.slice(1);
166
+ const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
167
+ if (markerMatch) {
168
+ const index = Number(markerMatch[1]);
169
+ const handler = values[index];
170
+ if (typeof handler === "function") {
171
+ bindEvent(el2, eventName, handler, modifiers, bindings);
172
+ }
173
+ }
174
+ attrsToRemove.push(name);
175
+ continue;
176
+ }
177
+ if (name.startsWith("class:")) {
178
+ const className = name.slice(6);
179
+ const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
180
+ if (markerMatch) {
181
+ const index = Number(markerMatch[1]);
182
+ const value = values[index];
183
+ bindClass(el2, className, value, bindings, disposers);
184
+ }
185
+ attrsToRemove.push(name);
186
+ continue;
187
+ }
188
+ if (name === "html:inner") {
189
+ const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
190
+ if (markerMatch) {
191
+ const index = Number(markerMatch[1]);
192
+ const value = values[index];
193
+ bindInnerHtml(el2, value, bindings, disposers);
194
+ }
195
+ attrsToRemove.push(name);
196
+ continue;
197
+ }
198
+ if (name === "ref" && attrValue.includes(MARKER_PREFIX)) {
199
+ const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
200
+ if (markerMatch) {
201
+ const index = Number(markerMatch[1]);
202
+ const value = values[index];
203
+ if (isRef(value)) {
204
+ value.current = el2;
205
+ disposers.push(() => {
206
+ value.current = null;
207
+ });
208
+ }
209
+ }
210
+ attrsToRemove.push(name);
211
+ continue;
212
+ }
213
+ if (attrValue.includes(MARKER_PREFIX)) {
214
+ const markers = [...attrValue.matchAll(/<!--bk-(\d+)-->/g)];
215
+ if (markers.length) {
216
+ const first = markers[0];
217
+ let value;
218
+ if (markers.length === 1 && first && attrValue === first[0]) {
219
+ value = values[Number(first[1])];
220
+ } else {
221
+ const resolve = () => attrValue.replace(/<!--bk-(\d+)-->/g, (_, i) => {
222
+ const v = values[Number(i)];
223
+ const raw2 = isSignal(v) ? v.value : v;
224
+ return raw2 == null || raw2 === false ? "" : String(raw2);
225
+ });
226
+ const hasSignals = markers.some((m) => isSignal(values[Number(m[1])]));
227
+ value = hasSignals ? computed(resolve) : resolve();
228
+ }
229
+ if (nativeFormProperty(el2, name)) {
230
+ (afterChildren ??= []).push(() => bindAttribute(el2, name, value, bindings, disposers, afterMount));
231
+ } else {
232
+ bindAttribute(el2, name, value, bindings, disposers, afterMount);
233
+ }
234
+ }
235
+ continue;
236
+ }
237
+ }
238
+ for (const name of attrsToRemove) {
239
+ el2.removeAttribute(name);
240
+ }
241
+ return afterChildren;
242
+ }
243
+ function mountFactoryResult(factory) {
244
+ const el2 = document.createElement(factory.tagName);
245
+ const localDisposers = [];
246
+ for (const [key, raw2] of Object.entries(factory.props)) {
247
+ if (isSignal(raw2)) {
248
+ const sig = raw2;
249
+ const unsub = sig.subscribe((newVal) => {
250
+ el2._setProp?.(key, newVal);
251
+ });
252
+ localDisposers.push(unsub);
253
+ } else {
254
+ el2._setProp?.(key, raw2);
255
+ }
256
+ }
257
+ if (factory.hostAttrs?.class != null) {
258
+ const classVal = factory.hostAttrs.class;
259
+ if (isSignal(classVal)) {
260
+ const sig = classVal;
261
+ let prevClasses = [];
262
+ const applyClasses = (raw2) => {
263
+ const next = raw2 ? raw2.split(/\s+/).filter(Boolean) : [];
264
+ for (const cls of prevClasses) {
265
+ if (!next.includes(cls)) el2.classList.remove(cls);
266
+ }
267
+ for (const cls of next) {
268
+ if (!prevClasses.includes(cls)) el2.classList.add(cls);
269
+ }
270
+ prevClasses = next;
271
+ };
272
+ const unsub = sig.subscribe(applyClasses);
273
+ localDisposers.push(unsub);
274
+ } else if (typeof classVal === "string" && classVal) {
275
+ for (const cls of classVal.split(/\s+/).filter(Boolean)) {
276
+ el2.classList.add(cls);
277
+ }
278
+ }
279
+ }
280
+ return {
281
+ element: el2,
282
+ dispose() {
283
+ for (const dispose of localDisposers.splice(0)) {
284
+ try {
285
+ dispose();
286
+ } catch (_) {
287
+ }
288
+ }
289
+ }
290
+ };
291
+ }
292
+ function replaceMarkerWithBinding(comment, value, bindings, disposers) {
293
+ const parent = comment.parentNode;
294
+ if (!parent) return;
295
+ if (isSignal(value)) {
296
+ const startMarker = document.createComment("slot-start");
297
+ const endMarker = document.createComment("slot-end");
298
+ parent.replaceChild(endMarker, comment);
299
+ parent.insertBefore(startMarker, endMarker);
300
+ let currentResults = [];
301
+ let currentNodes = [];
302
+ let currentFactoryDisposers = [];
303
+ let currentText = null;
304
+ const NOT_RENDERED = /* @__PURE__ */ Symbol("not-rendered");
305
+ let lastValue = NOT_RENDERED;
306
+ const dispose = effect(() => {
307
+ const newValue = value.value;
308
+ const liveParent = endMarker.parentNode;
309
+ if (!liveParent) return;
310
+ if (newValue === lastValue) return;
311
+ lastValue = newValue;
312
+ const isTemplate = Boolean(
313
+ newValue && typeof newValue === "object" && "__templateResult" in newValue
314
+ );
315
+ const isFactory = Boolean(
316
+ newValue && typeof newValue === "object" && "__type" in newValue && newValue.__type === "factory"
317
+ );
318
+ const isPrimitive = newValue != null && newValue !== false && !Array.isArray(newValue) && !isTemplate && !isFactory;
319
+ if (isPrimitive && currentText) {
320
+ currentText.data = String(newValue);
321
+ return;
322
+ }
323
+ for (const r of currentResults) {
324
+ try {
325
+ r.dispose();
326
+ } catch (_) {
327
+ }
328
+ }
329
+ for (const dispose2 of currentFactoryDisposers) {
330
+ try {
331
+ dispose2();
332
+ } catch (_) {
333
+ }
334
+ }
335
+ for (const node of currentNodes) {
336
+ node.parentNode?.removeChild(node);
337
+ }
338
+ currentNodes = [];
339
+ currentResults = [];
340
+ currentFactoryDisposers = [];
341
+ currentText = null;
342
+ if (newValue == null) {
343
+ } else if (Array.isArray(newValue)) {
344
+ for (const item of newValue) {
345
+ if (item && typeof item === "object" && "__templateResult" in item) {
346
+ const tpl = item;
347
+ const wrapper = document.createDocumentFragment();
348
+ tpl.mount(wrapper);
349
+ const nodes = [...wrapper.childNodes];
350
+ currentNodes.push(...nodes);
351
+ currentResults.push(tpl);
352
+ liveParent.insertBefore(wrapper, endMarker);
353
+ } else if (item && typeof item === "object" && "__type" in item && item.__type === "factory") {
354
+ const mounted = mountFactoryResult(item);
355
+ currentNodes.push(mounted.element);
356
+ currentFactoryDisposers.push(mounted.dispose);
357
+ liveParent.insertBefore(mounted.element, endMarker);
358
+ } else if (item != null && item !== false) {
359
+ const textNode = document.createTextNode(String(item));
360
+ currentNodes.push(textNode);
361
+ liveParent.insertBefore(textNode, endMarker);
362
+ }
363
+ }
364
+ } else if (isTemplate) {
365
+ const tpl = newValue;
366
+ const wrapper = document.createDocumentFragment();
367
+ tpl.mount(wrapper);
368
+ currentNodes = [...wrapper.childNodes];
369
+ currentResults.push(tpl);
370
+ liveParent.insertBefore(wrapper, endMarker);
371
+ } else if (isFactory) {
372
+ const mounted = mountFactoryResult(newValue);
373
+ currentNodes.push(mounted.element);
374
+ currentFactoryDisposers.push(mounted.dispose);
375
+ liveParent.insertBefore(mounted.element, endMarker);
376
+ } else if (newValue !== false) {
377
+ currentText = document.createTextNode(String(newValue));
378
+ currentNodes.push(currentText);
379
+ liveParent.insertBefore(currentText, endMarker);
380
+ }
381
+ });
382
+ disposers.push(dispose);
383
+ disposers.push(() => {
384
+ for (const r of currentResults.splice(0)) {
385
+ try {
386
+ r.dispose();
387
+ } catch (_) {
388
+ }
389
+ }
390
+ for (const factoryDispose of currentFactoryDisposers.splice(0)) {
391
+ try {
392
+ factoryDispose();
393
+ } catch (_) {
394
+ }
395
+ }
396
+ currentNodes = [];
397
+ currentText = null;
398
+ });
399
+ } else if (value && typeof value === "object" && "__templateResult" in value) {
400
+ const result = value;
401
+ const startMarker = document.createComment("tpl-start");
402
+ const endMarker = document.createComment("tpl-end");
403
+ parent.replaceChild(endMarker, comment);
404
+ parent.insertBefore(startMarker, endMarker);
405
+ const wrapper = document.createDocumentFragment();
406
+ result.mount(wrapper);
407
+ parent.insertBefore(wrapper, endMarker);
408
+ disposers.push(() => result.dispose());
409
+ } else if (value && typeof value === "object" && "__type" in value && value.__type === "factory") {
410
+ const mounted = mountFactoryResult(value);
411
+ parent.replaceChild(mounted.element, comment);
412
+ disposers.push(mounted.dispose);
413
+ } else if (Array.isArray(value)) {
414
+ const startMarker = document.createComment("list-start");
415
+ const endMarker = document.createComment("list-end");
416
+ parent.replaceChild(endMarker, comment);
417
+ parent.insertBefore(startMarker, endMarker);
418
+ for (const item of value) {
419
+ if (item && typeof item === "object" && "__templateResult" in item) {
420
+ const wrapper = document.createDocumentFragment();
421
+ item.mount(wrapper);
422
+ parent.insertBefore(wrapper, endMarker);
423
+ disposers.push(() => item.dispose());
424
+ } else if (item && typeof item === "object" && "__type" in item && item.__type === "factory") {
425
+ const mounted = mountFactoryResult(item);
426
+ parent.insertBefore(mounted.element, endMarker);
427
+ disposers.push(mounted.dispose);
428
+ } else {
429
+ const textNode = document.createTextNode(String(item));
430
+ parent.insertBefore(textNode, endMarker);
431
+ }
432
+ }
433
+ } else if (value == null || value === false) {
434
+ parent.removeChild(comment);
435
+ } else {
436
+ const textNode = document.createTextNode(String(value));
437
+ parent.replaceChild(textNode, comment);
438
+ }
439
+ }
440
+ function bindAttribute(el2, name, value, bindings, disposers, afterMount) {
441
+ if (name === "class") {
442
+ bindClassAttribute(el2, value, bindings, disposers);
443
+ return;
444
+ }
445
+ const isHtmlAttr = name === "id" || name === "style" || name === "slot" || name === "title" || name === "role" || name === "tabindex" || name === "name" || name.startsWith("data-") || name.startsWith("aria-");
446
+ const hasPropSetter = !isHtmlAttr && typeof el2._setProp === "function";
447
+ if (hasPropSetter) {
448
+ if (isSignal(value)) {
449
+ const binding = { type: "attribute", element: el2, name };
450
+ bindings.push(binding);
451
+ const dispose = effect(() => {
452
+ el2._setProp(name, value.value);
453
+ });
454
+ binding.dispose = dispose;
455
+ disposers.push(dispose);
456
+ } else {
457
+ el2._setProp(name, value);
458
+ }
459
+ return;
460
+ }
461
+ bindPlainAttribute(el2, name, value, bindings, disposers, afterMount);
462
+ }
463
+ function bindPlainAttribute(el2, name, value, bindings, disposers, afterMount) {
464
+ const property = nativeFormProperty(el2, name);
465
+ let current;
466
+ const apply = (v) => {
467
+ current = v;
468
+ if (v == null || v === false) {
469
+ el2.removeAttribute(name);
470
+ } else {
471
+ el2.setAttribute(name, v === true ? "" : String(v));
472
+ }
473
+ if (property) syncFormProperty(el2, property, v);
474
+ };
475
+ if (isSignal(value)) {
476
+ const binding = { type: "attribute", element: el2, name };
477
+ bindings.push(binding);
478
+ const dispose = effect(() => apply(value.value));
479
+ binding.dispose = dispose;
480
+ disposers.push(dispose);
481
+ } else {
482
+ apply(value);
483
+ }
484
+ if (property === "value" && el2.localName === "select") {
485
+ let active = true;
486
+ const sync = () => {
487
+ if (active) syncFormProperty(el2, "value", current);
488
+ };
489
+ const observer = new MutationObserver(sync);
490
+ observer.observe(el2, {
491
+ childList: true,
492
+ subtree: true,
493
+ characterData: true,
494
+ attributes: true,
495
+ attributeFilter: ["value", "selected"]
496
+ });
497
+ disposers.push(() => {
498
+ active = false;
499
+ observer.disconnect();
500
+ });
501
+ afterMount.push(sync);
502
+ queueMicrotask(sync);
503
+ }
504
+ }
505
+ function nativeFormProperty(el2, name) {
506
+ const attribute = name.toLowerCase();
507
+ if (attribute !== "value" && attribute !== "checked" && attribute !== "selected") return;
508
+ if (el2.namespaceURI !== "http://www.w3.org/1999/xhtml") return;
509
+ const tag = el2.localName;
510
+ if (attribute === "value" && (tag === "input" || tag === "textarea" || tag === "select")) return attribute;
511
+ if (attribute === "checked" && tag === "input") return attribute;
512
+ if (attribute === "selected" && tag === "option") return attribute;
513
+ }
514
+ function syncFormProperty(el2, property, value) {
515
+ if (property === "value") {
516
+ const control = el2;
517
+ const next = value == null || typeof value === "boolean" ? "" : String(value);
518
+ if (el2.localName === "input" && el2.type === "file" && next !== "") return;
519
+ if (el2.localName === "select" || control.value !== next) control.value = next;
520
+ } else {
521
+ const control = el2;
522
+ const next = value != null && value !== false;
523
+ if (control[property] !== next) control[property] = next;
524
+ }
525
+ }
526
+ function bindClassAttribute(el2, value, bindings, disposers) {
527
+ let initialized = false;
528
+ let prevClasses = [];
529
+ const applyClasses = (raw2) => {
530
+ if (!initialized) {
531
+ el2.setAttribute("class", "");
532
+ initialized = true;
533
+ }
534
+ const str = raw2 == null || raw2 === false ? "" : String(raw2);
535
+ const next = str.split(/\s+/).filter(Boolean);
536
+ for (const cls of prevClasses) {
537
+ if (!next.includes(cls)) {
538
+ el2.classList.remove(cls);
539
+ }
540
+ }
541
+ for (const cls of next) {
542
+ if (!prevClasses.includes(cls)) {
543
+ el2.classList.add(cls);
544
+ }
545
+ }
546
+ prevClasses = next;
547
+ };
548
+ if (isSignal(value)) {
549
+ const binding = { type: "attribute", element: el2, name: "class" };
550
+ bindings.push(binding);
551
+ const dispose = effect(() => {
552
+ applyClasses(value.value);
553
+ });
554
+ binding.dispose = dispose;
555
+ disposers.push(dispose);
556
+ } else {
557
+ applyClasses(value);
558
+ }
559
+ }
560
+ function bindClass(el2, className, value, bindings, disposers) {
561
+ if (isSignal(value)) {
562
+ const binding = { type: "class", element: el2, className };
563
+ bindings.push(binding);
564
+ const dispose = effect(() => {
565
+ const v = value.value;
566
+ el2.classList.toggle(className, !!v);
567
+ });
568
+ binding.dispose = dispose;
569
+ disposers.push(dispose);
570
+ } else {
571
+ el2.classList.toggle(className, !!value);
572
+ }
573
+ }
574
+ function bindInnerHtml(el2, value, bindings, disposers) {
575
+ const resolveHtml = (v) => {
576
+ if (v == null) return "";
577
+ if (isRawHtml(v)) {
578
+ if (isSanitizedHtml(v)) {
579
+ throw nisliError(
580
+ "N106",
581
+ `html:inner rejects a value branded both raw() and sanitized() \u2014 the trust decision is ambiguous. Pick one brand.`
582
+ );
583
+ }
584
+ return v.value;
585
+ }
586
+ if (isSanitizedHtml(v)) {
587
+ if (sanitizerFor(el2) === null) {
588
+ throw nisliError(
589
+ "N107",
590
+ `html:inner cannot render sanitized() markup: this engine has no native Element.setHTML() and no fallback is registered. Wire one at startup \u2014 setSanitizerFallback((el, html) => { el.innerHTML = DOMPurify.sanitize(html) }) \u2014 nisli ships no sanitizer and will not fall back to innerHTML.`
591
+ );
592
+ }
593
+ return v;
594
+ }
595
+ if (isTrustedHtml(v)) return v;
596
+ throw nisliError(
597
+ "N106",
598
+ `html:inner rejects unbranded values (got ${typeof v}). Wrap trusted markup in raw(html), untrusted markup in sanitized(html) \u2014 and never wrap user-generated input in raw().`
599
+ );
600
+ };
601
+ const writeHtml = (v) => {
602
+ const resolved = resolveHtml(v);
603
+ if (isSanitizedHtml(resolved)) {
604
+ sanitizerFor(el2)?.(el2, resolved.value);
605
+ return;
606
+ }
607
+ el2.innerHTML = resolved;
608
+ };
609
+ if (isSignal(value)) {
610
+ untracked(() => resolveHtml(value.value));
611
+ const binding = { type: "innerHtml", element: el2 };
612
+ bindings.push(binding);
613
+ const dispose = effect(() => {
614
+ writeHtml(value.value);
615
+ });
616
+ binding.dispose = dispose;
617
+ disposers.push(dispose);
618
+ } else {
619
+ writeHtml(value);
620
+ }
621
+ }
622
+ function bindEvent(el2, eventName, handler, modifiers, bindings) {
623
+ let safeHandler;
624
+ let composed = handler;
625
+ if (modifiers.includes("stop")) {
626
+ const inner = composed;
627
+ composed = (e) => {
628
+ e.stopPropagation();
629
+ inner(e);
630
+ };
631
+ }
632
+ if (modifiers.includes("prevent")) {
633
+ const inner = composed;
634
+ composed = (e) => {
635
+ e.preventDefault();
636
+ inner(e);
637
+ };
638
+ }
639
+ if (modifiers.includes("once")) {
640
+ const inner = composed;
641
+ composed = (e) => {
642
+ el2.removeEventListener(eventName, safeHandler);
643
+ inner(e);
644
+ };
645
+ }
646
+ for (const mod of modifiers) {
647
+ if (["enter", "escape", "space", "tab"].includes(mod)) {
648
+ const keyMap = {
649
+ enter: "Enter",
650
+ escape: "Escape",
651
+ space: " ",
652
+ tab: "Tab"
653
+ };
654
+ const inner = composed;
655
+ composed = (e) => {
656
+ if (e.key === keyMap[mod]) {
657
+ inner(e);
658
+ }
659
+ };
660
+ }
661
+ }
662
+ safeHandler = (e) => {
663
+ try {
664
+ untrack(() => composed(e));
665
+ } catch (err) {
666
+ console.error(`Event handler error for '${eventName}':`, err);
667
+ }
668
+ };
669
+ el2.addEventListener(eventName, safeHandler);
670
+ bindings.push({
671
+ type: "event",
672
+ element: el2,
673
+ eventName,
674
+ handler: safeHandler,
675
+ modifiers,
676
+ dispose: () => el2.removeEventListener(eventName, safeHandler)
677
+ });
678
+ }
679
+ const EACH_BRAND = /* @__PURE__ */ Symbol.for("backlog.each");
680
+ function asTemplateResult(result) {
681
+ if (result && typeof result === "object" && "__templateResult" in result) {
682
+ return result;
683
+ }
684
+ if (result && typeof result === "object" && result.__type === "factory") {
685
+ let mounted = null;
686
+ return {
687
+ __templateResult: true,
688
+ mount(host) {
689
+ mounted = mountFactoryResult(result);
690
+ host.appendChild(mounted.element);
691
+ },
692
+ dispose() {
693
+ mounted?.dispose();
694
+ mounted?.element.remove();
695
+ mounted = null;
696
+ }
697
+ };
698
+ }
699
+ throw new TypeError(
700
+ `each(): template callback must return a TemplateResult or a component factory result, got ${result === null ? "null" : typeof result}`
701
+ );
702
+ }
703
+ function each(items, keyFn, templateFn) {
704
+ let startMarker;
705
+ let endMarker;
706
+ let entries = [];
707
+ let effectDispose = null;
708
+ return {
709
+ __templateResult: true,
710
+ mount(host) {
711
+ startMarker = document.createComment("each-start");
712
+ endMarker = document.createComment("each-end");
713
+ host.appendChild(startMarker);
714
+ host.appendChild(endMarker);
715
+ effectDispose = effect(() => {
716
+ const newItems = items.value;
717
+ reconcile(newItems);
718
+ });
719
+ },
720
+ dispose() {
721
+ if (effectDispose) {
722
+ effectDispose();
723
+ effectDispose = null;
724
+ }
725
+ for (const entry of entries) {
726
+ entry.templateResult.dispose();
727
+ }
728
+ entries = [];
729
+ }
730
+ };
731
+ function reconcile(newItems) {
732
+ const parent = endMarker.parentNode;
733
+ if (!parent) return;
734
+ const keyedItems = [];
735
+ const keyIndices = /* @__PURE__ */ new Map();
736
+ for (let i = 0; i < newItems.length; i++) {
737
+ const item = newItems[i];
738
+ if (item === void 0) continue;
739
+ const key = keyFn(item, i);
740
+ const previousIndex = keyIndices.get(key);
741
+ if (previousIndex !== void 0) {
742
+ const formattedKey = typeof key === "string" ? JSON.stringify(key) : String(key);
743
+ console.error(
744
+ `each() requires unique keys. Duplicate key ${formattedKey} found at indices ${previousIndex} and ${i}; reconciliation skipped.`
745
+ );
746
+ return;
747
+ }
748
+ keyIndices.set(key, i);
749
+ keyedItems.push({ item, index: i, key });
750
+ }
751
+ const oldMap = /* @__PURE__ */ new Map();
752
+ for (const entry of entries) {
753
+ oldMap.set(entry.key, entry);
754
+ }
755
+ const newEntries = [];
756
+ const newKeys = /* @__PURE__ */ new Set();
757
+ for (const { item, index, key } of keyedItems) {
758
+ newKeys.add(key);
759
+ const existing = oldMap.get(key);
760
+ if (existing) {
761
+ existing.itemSignal.value = item;
762
+ existing.indexSignal.value = index;
763
+ newEntries.push(existing);
764
+ } else {
765
+ const itemSignal = signal(item);
766
+ const indexSignal = signal(index);
767
+ const wrapper = document.createElement("each-item");
768
+ wrapper.style.display = "contents";
769
+ const templateResult = asTemplateResult(templateFn(itemSignal, indexSignal));
770
+ templateResult.mount(wrapper);
771
+ newEntries.push({ key, itemSignal, indexSignal, templateResult, wrapper });
772
+ }
773
+ }
774
+ for (const entry of entries) {
775
+ if (!newKeys.has(entry.key)) {
776
+ entry.templateResult.dispose();
777
+ entry.wrapper.parentNode?.removeChild(entry.wrapper);
778
+ }
779
+ }
780
+ const atomic = canMoveWithin(parent);
781
+ let focusedIndex = -1;
782
+ if (!atomic) {
783
+ const root = parent.getRootNode();
784
+ let active = "activeElement" in root ? root.activeElement : null;
785
+ while (active && active.parentNode !== parent) active = active.parentElement;
786
+ if (active) focusedIndex = newEntries.findIndex((entry) => entry.wrapper === active);
787
+ }
788
+ if (focusedIndex !== -1) {
789
+ let next = newEntries[focusedIndex].wrapper;
790
+ for (let i = focusedIndex - 1; i >= 0; i--) {
791
+ const wrapper = newEntries[i].wrapper;
792
+ if (wrapper.nextSibling !== next) parent.insertBefore(wrapper, next);
793
+ next = wrapper;
794
+ }
795
+ }
796
+ let cursor = focusedIndex === -1 ? startMarker : newEntries[focusedIndex].wrapper;
797
+ for (let i = focusedIndex + 1; i < newEntries.length; i++) {
798
+ const entry = newEntries[i];
799
+ const nextSibling = cursor.nextSibling;
800
+ if (nextSibling !== entry.wrapper) {
801
+ if (entry.wrapper.parentNode === parent && atomic) {
802
+ parent.moveBefore(entry.wrapper, nextSibling);
803
+ } else {
804
+ parent.insertBefore(entry.wrapper, nextSibling);
805
+ }
806
+ }
807
+ cursor = entry.wrapper;
808
+ }
809
+ entries = newEntries;
810
+ }
811
+ }
812
+ function when(condition, template, elseTemplate) {
813
+ const resolveBranch = (branch) => {
814
+ if (branch === void 0) return null;
815
+ if (typeof branch === "function") return untracked(branch);
816
+ return branch;
817
+ };
818
+ if (isSignal(condition)) {
819
+ let lastGate;
820
+ let lastResult = null;
821
+ return computed(() => {
822
+ const gate = !!condition.value;
823
+ if (gate === lastGate) return lastResult;
824
+ const next = gate ? resolveBranch(template) : resolveBranch(elseTemplate);
825
+ lastGate = gate;
826
+ lastResult = next;
827
+ return next;
828
+ });
829
+ }
830
+ return condition ? resolveBranch(template) : resolveBranch(elseTemplate);
831
+ }
832
+ function el(tag, props = {}, children) {
833
+ let node = null;
834
+ const bindings = [];
835
+ const disposers = [];
836
+ return {
837
+ __templateResult: true,
838
+ mount(host) {
839
+ const element = document.createElement(tag);
840
+ node = element;
841
+ let afterChildren;
842
+ const afterMount = [];
843
+ for (const [key, val] of Object.entries(props)) {
844
+ const formProperty = nativeFormProperty(element, key);
845
+ if (val === void 0 && !formProperty) continue;
846
+ if (key === "ref") {
847
+ if (isRef(val)) {
848
+ val.current = element;
849
+ disposers.push(() => {
850
+ val.current = null;
851
+ });
852
+ }
853
+ continue;
854
+ }
855
+ if (key === "on") {
856
+ for (const [event, handler] of Object.entries(val)) {
857
+ bindEvent(element, event, handler, [], bindings);
858
+ }
859
+ continue;
860
+ }
861
+ if (key === "class") {
862
+ bindClassAttribute(element, val, bindings, disposers);
863
+ continue;
864
+ }
865
+ if (formProperty) {
866
+ (afterChildren ??= []).push(() => bindPlainAttribute(element, key, val, bindings, disposers, afterMount));
867
+ } else {
868
+ bindPlainAttribute(element, key, val, bindings, disposers, afterMount);
869
+ }
870
+ }
871
+ const flat = [];
872
+ flattenChildren(children, flat);
873
+ for (const child of flat) {
874
+ const anchor = document.createComment("");
875
+ element.appendChild(anchor);
876
+ replaceMarkerWithBinding(anchor, child, bindings, disposers);
877
+ }
878
+ if (afterChildren) for (const bind of afterChildren) bind();
879
+ host.appendChild(element);
880
+ for (const sync of afterMount) sync();
881
+ },
882
+ dispose() {
883
+ for (const d of disposers) {
884
+ try {
885
+ d();
886
+ } catch (_) {
887
+ }
888
+ }
889
+ disposers.length = 0;
890
+ for (const b of bindings) {
891
+ if ("dispose" in b && b.dispose) {
892
+ try {
893
+ b.dispose();
894
+ } catch (_) {
895
+ }
896
+ }
897
+ }
898
+ bindings.length = 0;
899
+ node?.parentNode?.removeChild(node);
900
+ node = null;
901
+ }
902
+ };
903
+ }
904
+ function flattenChildren(children, out) {
905
+ if (children === void 0) return;
906
+ if (Array.isArray(children)) {
907
+ for (const c of children) flattenChildren(c, out);
908
+ } else {
909
+ out.push(children);
910
+ }
911
+ }
912
+ export {
913
+ __templateParseCount,
914
+ each,
915
+ el,
916
+ html,
917
+ raw,
918
+ sanitized,
919
+ setSanitizerFallback,
920
+ when
921
+ };
922
+ //# sourceMappingURL=template.js.map