sibujs 4.5.0 → 4.7.0

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 (116) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +326 -133
  3. package/dist/browser.d.cts +46 -14
  4. package/dist/browser.d.ts +46 -14
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +2094 -1033
  7. package/dist/build.d.cts +133 -44
  8. package/dist/build.d.ts +133 -44
  9. package/dist/build.js +1372 -574
  10. package/dist/cdn.dev.global.js +10 -10
  11. package/dist/cdn.full.dev.global.js +11 -11
  12. package/dist/cdn.full.global.js +10 -10
  13. package/dist/cdn.global.js +10 -10
  14. package/dist/{chunk-2DCGACUU.js → chunk-2BPG2XDA.js} +251 -66
  15. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  16. package/dist/{chunk-4PMLNECI.js → chunk-3QWBSL5R.js} +98 -29
  17. package/dist/{chunk-KKLW7YWL.js → chunk-4AWA2PVD.js} +275 -144
  18. package/dist/chunk-5HZXGZ6T.js +24 -0
  19. package/dist/{chunk-PCT43HW3.js → chunk-7LUJQAOJ.js} +1 -1
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/chunk-BC2SECJD.js +44 -0
  22. package/dist/{chunk-TBYTO6BS.js → chunk-BSY63EM6.js} +136 -80
  23. package/dist/{chunk-FTIR4QW2.js → chunk-DKXRACVN.js} +6 -8
  24. package/dist/{chunk-ONOHFDLG.js → chunk-FNJXNGYZ.js} +3 -3
  25. package/dist/{chunk-XZZOBQAY.js → chunk-FQRUXCEE.js} +6 -5
  26. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-UGRX3S57.js → chunk-JWKYU5GV.js} +22 -4
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-RBTPLM32.js → chunk-RUSSKG6G.js} +13 -12
  31. package/dist/{chunk-7LN645I6.js → chunk-SLM3IA34.js} +3 -3
  32. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  33. package/dist/{chunk-B3WHI2QA.js → chunk-UNWRJRKC.js} +55 -31
  34. package/dist/{chunk-KEISJXBU.js → chunk-UOL2ECCS.js} +44 -17
  35. package/dist/{chunk-RJE2BNI4.js → chunk-VZSG24LS.js} +108 -45
  36. package/dist/{chunk-RIXRAYIU.js → chunk-WEQ3DMVL.js} +10 -4
  37. package/dist/{chunk-3JZ4L5TJ.js → chunk-WOLJZUFQ.js} +354 -90
  38. package/dist/{chunk-S373NSMK.js → chunk-XYV3EDB7.js} +283 -123
  39. package/dist/{chunk-VZKNK2V7.js → chunk-XZR4PXRE.js} +296 -38
  40. package/dist/{chunk-GW3SCCZG.js → chunk-Z3OHK6QT.js} +153 -110
  41. package/dist/{chunk-OMJJM3KM.js → chunk-ZVL7TY4K.js} +287 -130
  42. package/dist/{contracts-DBdg9J_a.d.ts → contracts-CLqzJnOV.d.ts} +36 -17
  43. package/dist/{contracts-DBdg9J_a.d.cts → contracts-CTOJXu-x.d.cts} +36 -17
  44. package/dist/{customElement-OB9CIsc5.d.cts → customElement-MmInOW1U.d.cts} +21 -0
  45. package/dist/{customElement-OB9CIsc5.d.ts → customElement-MmInOW1U.d.ts} +21 -0
  46. package/dist/data.cjs +410 -155
  47. package/dist/data.d.cts +166 -12
  48. package/dist/data.d.ts +166 -12
  49. package/dist/data.js +12 -9
  50. package/dist/devtools.cjs +98 -61
  51. package/dist/devtools.js +7 -8
  52. package/dist/dispose-GEIG2KOF.js +28 -0
  53. package/dist/ecosystem.cjs +372 -126
  54. package/dist/ecosystem.d.cts +20 -3
  55. package/dist/ecosystem.d.ts +20 -3
  56. package/dist/ecosystem.js +12 -12
  57. package/dist/extras.cjs +2366 -909
  58. package/dist/extras.d.cts +11 -9
  59. package/dist/extras.d.ts +11 -9
  60. package/dist/extras.js +39 -28
  61. package/dist/index.cjs +421 -158
  62. package/dist/index.d.cts +213 -171
  63. package/dist/index.d.ts +213 -171
  64. package/dist/index.js +24 -27
  65. package/dist/motion.cjs +118 -44
  66. package/dist/motion.js +5 -5
  67. package/dist/patterns.cjs +344 -53
  68. package/dist/patterns.d.cts +28 -9
  69. package/dist/patterns.d.ts +28 -9
  70. package/dist/patterns.js +8 -8
  71. package/dist/performance.cjs +325 -220
  72. package/dist/performance.d.cts +2 -2
  73. package/dist/performance.d.ts +2 -2
  74. package/dist/performance.js +8 -9
  75. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  76. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  77. package/dist/plugins.cjs +664 -233
  78. package/dist/plugins.d.cts +127 -14
  79. package/dist/plugins.d.ts +127 -14
  80. package/dist/plugins.js +96 -42
  81. package/dist/signal-EotCj4hS.d.cts +110 -0
  82. package/dist/signal-EotCj4hS.d.ts +110 -0
  83. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  84. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  85. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  86. package/dist/ssr.cjs +229 -84
  87. package/dist/ssr.d.cts +9 -3
  88. package/dist/ssr.d.ts +9 -3
  89. package/dist/ssr.js +11 -12
  90. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  91. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  92. package/dist/tagFactory-8qL9LCIx.d.cts +156 -0
  93. package/dist/tagFactory-BL2fymez.d.ts +156 -0
  94. package/dist/testing.cjs +2503 -2191
  95. package/dist/testing.d.cts +56 -5
  96. package/dist/testing.d.ts +56 -5
  97. package/dist/testing.js +580 -307
  98. package/dist/types-CJFViL6Q.d.cts +26 -0
  99. package/dist/types-CJFViL6Q.d.ts +26 -0
  100. package/dist/ui.cjs +732 -329
  101. package/dist/ui.d.cts +41 -7
  102. package/dist/ui.d.ts +41 -7
  103. package/dist/ui.js +151 -56
  104. package/dist/widgets.cjs +267 -291
  105. package/dist/widgets.js +9 -10
  106. package/package.json +4 -2
  107. package/dist/chunk-2WLZ6757.js +0 -149
  108. package/dist/chunk-CCSJMTRN.js +0 -15
  109. package/dist/chunk-QKRPLZ2V.js +0 -108
  110. package/dist/chunk-VUF4ALSW.js +0 -60
  111. package/dist/chunk-WWV3SJ3L.js +0 -131
  112. package/dist/dispose-46BOMMQJ.js +0 -19
  113. package/dist/plugin-D30wlGW5.d.cts +0 -71
  114. package/dist/plugin-D30wlGW5.d.ts +0 -71
  115. package/dist/tagFactory-DVoDpHye.d.cts +0 -215
  116. package/dist/tagFactory-DVoDpHye.d.ts +0 -215
package/dist/build.cjs CHANGED
@@ -59,8 +59,1162 @@ __export(build_exports, {
59
59
  });
60
60
  module.exports = __toCommonJS(build_exports);
61
61
 
62
- // src/build/compileTemplates.ts
62
+ // src/build/sourceEdit.ts
63
+ function isLineBreak(c) {
64
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
65
+ }
66
+ function lineBreaksIn(code2, start, end) {
67
+ let out = "";
68
+ for (let i2 = start; i2 < end; i2++) {
69
+ if (isLineBreak(code2.charCodeAt(i2))) out += code2[i2];
70
+ }
71
+ return out;
72
+ }
73
+ var LINE_SEPARATOR = String.fromCharCode(8232);
74
+ var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
75
+ function jsStringLiteral(value) {
76
+ const json = JSON.stringify(value);
77
+ if (!json.includes(LINE_SEPARATOR) && !json.includes(PARAGRAPH_SEPARATOR)) return json;
78
+ const bs = "\\";
79
+ return json.split(LINE_SEPARATOR).join(`${bs}u2028`).split(PARAGRAPH_SEPARATOR).join(`${bs}u2029`);
80
+ }
81
+ function normalizeEdits(edits) {
82
+ const sorted = edits.map((edit, order) => ({ edit, order })).sort(
83
+ (a2, b2) => a2.edit.start - b2.edit.start || a2.edit.end - a2.edit.start - (b2.edit.end - b2.edit.start) || a2.order - b2.order
84
+ ).map((x) => x.edit);
85
+ for (let i2 = 1; i2 < sorted.length; i2++) {
86
+ if (sorted[i2].start < sorted[i2 - 1].end) return null;
87
+ }
88
+ return sorted;
89
+ }
90
+ function applyEdits(code2, edits, append = "") {
91
+ let out = "";
92
+ let pos = 0;
93
+ for (const edit of edits) {
94
+ out += code2.slice(pos, edit.start) + edit.text;
95
+ pos = edit.end;
96
+ }
97
+ return out + code2.slice(pos) + append;
98
+ }
99
+ var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
100
+ function vlq(value) {
101
+ let v = value < 0 ? -value << 1 | 1 : value << 1;
102
+ let out = "";
103
+ do {
104
+ let digit = v & 31;
105
+ v >>>= 5;
106
+ if (v > 0) digit |= 32;
107
+ out += BASE64[digit];
108
+ } while (v > 0);
109
+ return out;
110
+ }
111
+ function isWordStart(c) {
112
+ return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127 || c >= 48 && c <= 57;
113
+ }
114
+ function editsSourceMap(code2, edits, append, source2) {
115
+ const lineStarts = [0];
116
+ for (let i2 = 0; i2 < code2.length; i2++) if (code2.charCodeAt(i2) === 10) lineStarts.push(i2 + 1);
117
+ const origLine = (offset) => {
118
+ let lo = 0;
119
+ let hi = lineStarts.length - 1;
120
+ while (lo < hi) {
121
+ const mid = lo + hi + 1 >> 1;
122
+ if (lineStarts[mid] <= offset) lo = mid;
123
+ else hi = mid - 1;
124
+ }
125
+ return lo;
126
+ };
127
+ const lines = [[]];
128
+ let col2 = 0;
129
+ const mark2 = (offset) => {
130
+ const line2 = lines[lines.length - 1];
131
+ const last = line2[line2.length - 1];
132
+ if (last && last[0] === col2) return;
133
+ const ol2 = origLine(offset);
134
+ line2.push([col2, ol2, offset - lineStarts[ol2]]);
135
+ };
136
+ const newline = () => {
137
+ lines.push([]);
138
+ col2 = 0;
139
+ };
140
+ const emitOriginal = (from, to) => {
141
+ for (let i2 = from; i2 < to; i2++) {
142
+ const c = code2.charCodeAt(i2);
143
+ if (c === 10) {
144
+ newline();
145
+ continue;
146
+ }
147
+ if (i2 === from || col2 === 0 || isWordStart(c) && !isWordStart(code2.charCodeAt(i2 - 1))) mark2(i2);
148
+ col2++;
149
+ }
150
+ };
151
+ const emitReplacement = (edit) => {
152
+ let origOffset = edit.start;
153
+ let pending = true;
154
+ for (let i2 = 0; i2 < edit.text.length; i2++) {
155
+ if (edit.text.charCodeAt(i2) === 10) {
156
+ newline();
157
+ const nl = code2.indexOf("\n", origOffset);
158
+ if (nl !== -1 && nl < edit.end) {
159
+ origOffset = nl + 1;
160
+ pending = true;
161
+ } else {
162
+ pending = false;
163
+ }
164
+ continue;
165
+ }
166
+ if (pending) {
167
+ mark2(origOffset);
168
+ pending = false;
169
+ }
170
+ col2++;
171
+ }
172
+ };
173
+ let pos = 0;
174
+ for (const edit of edits) {
175
+ emitOriginal(pos, edit.start);
176
+ emitReplacement(edit);
177
+ pos = edit.end;
178
+ }
179
+ emitOriginal(pos, code2.length);
180
+ for (let i2 = 0; i2 < append.length; i2++) {
181
+ if (append.charCodeAt(i2) === 10) newline();
182
+ }
183
+ let prevLine = 0;
184
+ let prevCol = 0;
185
+ let prevSource = 0;
186
+ const mappings = lines.map((segments) => {
187
+ let prevGen = 0;
188
+ return segments.map(([gen, ol2, oc]) => {
189
+ const s2 = vlq(gen - prevGen) + vlq(0 - prevSource) + vlq(ol2 - prevLine) + vlq(oc - prevCol);
190
+ prevGen = gen;
191
+ prevSource = 0;
192
+ prevLine = ol2;
193
+ prevCol = oc;
194
+ return s2;
195
+ }).join(",");
196
+ }).join(";");
197
+ return { version: 3, sources: [source2], sourcesContent: [code2], names: [], mappings };
198
+ }
199
+
200
+ // src/build/sourceScan.ts
201
+ var REGEX_AFTER_KEYWORD = /* @__PURE__ */ new Set([
202
+ "return",
203
+ "typeof",
204
+ "instanceof",
205
+ "in",
206
+ "of",
207
+ "new",
208
+ "delete",
209
+ "void",
210
+ "throw",
211
+ "case",
212
+ "do",
213
+ "else",
214
+ "yield",
215
+ "await"
216
+ ]);
217
+ function isIdentStart(c) {
218
+ return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127;
219
+ }
220
+ function isIdentPart(c) {
221
+ return isIdentStart(c) || c >= 48 && c <= 57;
222
+ }
223
+ function isDigit(c) {
224
+ return c >= 48 && c <= 57;
225
+ }
226
+ var ScanError = class extends Error {
227
+ };
228
+ var scanStats = { tokenize: 0 };
229
+ function tokenize(code2) {
230
+ scanStats.tokenize++;
231
+ let pos = 0;
232
+ const len = code2.length;
233
+ if (code2.startsWith("#!")) {
234
+ const nl = code2.indexOf("\n");
235
+ pos = nl === -1 ? len : nl;
236
+ }
237
+ function regexAllowed(prev) {
238
+ if (!prev) return true;
239
+ switch (prev.type) {
240
+ case "number":
241
+ case "string":
242
+ case "regex":
243
+ case "template":
244
+ return false;
245
+ case "ident":
246
+ return REGEX_AFTER_KEYWORD.has(prev.value);
247
+ case "punct":
248
+ return prev.value !== ")" && prev.value !== "]";
249
+ }
250
+ }
251
+ function skipTrivia() {
252
+ while (pos < len) {
253
+ const c = code2.charCodeAt(pos);
254
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 || c === 160 || c === 65279) {
255
+ pos++;
256
+ } else if (c === 8232 || c === 8233) {
257
+ pos++;
258
+ } else if (c === 47 && code2.charCodeAt(pos + 1) === 47) {
259
+ pos += 2;
260
+ while (pos < len) {
261
+ const d = code2.charCodeAt(pos);
262
+ if (d === 10 || d === 13 || d === 8232 || d === 8233) break;
263
+ pos++;
264
+ }
265
+ } else if (c === 47 && code2.charCodeAt(pos + 1) === 42) {
266
+ const end = code2.indexOf("*/", pos + 2);
267
+ if (end === -1) throw new ScanError("unterminated comment");
268
+ pos = end + 2;
269
+ } else {
270
+ break;
271
+ }
272
+ }
273
+ }
274
+ function readString() {
275
+ const start = pos;
276
+ const quote2 = code2[pos];
277
+ pos++;
278
+ while (pos < len) {
279
+ const c = code2[pos];
280
+ if (c === "\\") {
281
+ pos += 2;
282
+ continue;
283
+ }
284
+ if (c === quote2) {
285
+ pos++;
286
+ return { type: "string", start, end: pos };
287
+ }
288
+ if (c === "\n" || c === "\r") throw new ScanError("line break in string");
289
+ pos++;
290
+ }
291
+ throw new ScanError("unterminated string");
292
+ }
293
+ function readRegex() {
294
+ const start = pos;
295
+ pos++;
296
+ let inClass = false;
297
+ while (pos < len) {
298
+ const c = code2[pos];
299
+ if (c === "\\") {
300
+ pos += 2;
301
+ continue;
302
+ }
303
+ if (c === "\n" || c === "\r") throw new ScanError("line break in regex");
304
+ if (inClass) {
305
+ if (c === "]") inClass = false;
306
+ } else if (c === "[") {
307
+ inClass = true;
308
+ } else if (c === "/") {
309
+ pos++;
310
+ while (pos < len && isIdentPart(code2.charCodeAt(pos))) pos++;
311
+ return { type: "regex", start, end: pos };
312
+ }
313
+ pos++;
314
+ }
315
+ throw new ScanError("unterminated regex");
316
+ }
317
+ function readTemplate() {
318
+ const start = pos;
319
+ pos++;
320
+ const quasis = [];
321
+ const exprs = [];
322
+ let quasiStart = pos;
323
+ while (pos < len) {
324
+ const c = code2[pos];
325
+ if (c === "\\") {
326
+ pos += 2;
327
+ continue;
328
+ }
329
+ if (c === "`") {
330
+ quasis.push({ start: quasiStart, end: pos });
331
+ pos++;
332
+ return { type: "template", start, end: pos, quasis, exprs };
333
+ }
334
+ if (c === "$" && code2[pos + 1] === "{") {
335
+ quasis.push({ start: quasiStart, end: pos });
336
+ pos += 2;
337
+ const exprStart = pos;
338
+ const tokens = readList(true);
339
+ exprs.push({ start: exprStart, end: pos, tokens });
340
+ pos++;
341
+ quasiStart = pos;
342
+ continue;
343
+ }
344
+ pos++;
345
+ }
346
+ throw new ScanError("unterminated template");
347
+ }
348
+ function readList(inTemplateExpr) {
349
+ const tokens = [];
350
+ let depth = 0;
351
+ for (; ; ) {
352
+ skipTrivia();
353
+ if (pos >= len) {
354
+ if (inTemplateExpr || depth !== 0) throw new ScanError("unexpected end of input");
355
+ return tokens;
356
+ }
357
+ const c = code2.charCodeAt(pos);
358
+ const prev = tokens[tokens.length - 1];
359
+ if (c === 34 || c === 39) {
360
+ tokens.push(readString());
361
+ } else if (c === 96) {
362
+ tokens.push(readTemplate());
363
+ } else if (isIdentStart(c) || c === 92) {
364
+ const start = pos;
365
+ pos++;
366
+ while (pos < len) {
367
+ const d = code2.charCodeAt(pos);
368
+ if (isIdentPart(d)) pos++;
369
+ else if (d === 92) pos += 2;
370
+ else break;
371
+ }
372
+ tokens.push({ type: "ident", start, end: pos, value: code2.slice(start, pos) });
373
+ } else if (isDigit(c) || c === 46 && isDigit(code2.charCodeAt(pos + 1))) {
374
+ const start = pos;
375
+ pos++;
376
+ while (pos < len) {
377
+ const d = code2.charCodeAt(pos);
378
+ if (isIdentPart(d) || d === 46) pos++;
379
+ else break;
380
+ }
381
+ tokens.push({ type: "number", start, end: pos, value: code2.slice(start, pos) });
382
+ } else if (c === 47 && regexAllowed(prev)) {
383
+ tokens.push(readRegex());
384
+ } else {
385
+ const start = pos;
386
+ let value = code2[pos];
387
+ if (code2.startsWith("...", pos)) value = "...";
388
+ else if (code2.startsWith("?.", pos) && !isDigit(code2.charCodeAt(pos + 2))) value = "?.";
389
+ else if (code2.startsWith("=>", pos)) value = "=>";
390
+ pos += value.length;
391
+ if (value === "{") {
392
+ depth++;
393
+ } else if (value === "}") {
394
+ if (depth === 0) {
395
+ if (inTemplateExpr) {
396
+ pos = start;
397
+ return tokens;
398
+ }
399
+ throw new ScanError("unbalanced brace");
400
+ }
401
+ depth--;
402
+ }
403
+ tokens.push({ type: "punct", start, end: pos, value });
404
+ }
405
+ }
406
+ }
407
+ try {
408
+ return readList(false);
409
+ } catch (err) {
410
+ if (err instanceof ScanError) return null;
411
+ throw err;
412
+ }
413
+ }
414
+ function cookEscapes(raw) {
415
+ let out = "";
416
+ let i2 = 0;
417
+ const n = raw.length;
418
+ while (i2 < n) {
419
+ const ch = raw[i2];
420
+ if (ch === "\r") {
421
+ out += "\n";
422
+ i2 += raw[i2 + 1] === "\n" ? 2 : 1;
423
+ continue;
424
+ }
425
+ if (ch !== "\\") {
426
+ out += ch;
427
+ i2++;
428
+ continue;
429
+ }
430
+ const e = raw[i2 + 1];
431
+ i2 += 2;
432
+ switch (e) {
433
+ case "n":
434
+ out += "\n";
435
+ break;
436
+ case "t":
437
+ out += " ";
438
+ break;
439
+ case "r":
440
+ out += "\r";
441
+ break;
442
+ case "b":
443
+ out += "\b";
444
+ break;
445
+ case "f":
446
+ out += "\f";
447
+ break;
448
+ case "v":
449
+ out += "\v";
450
+ break;
451
+ case "0":
452
+ if (i2 < n && isDigit(raw.charCodeAt(i2))) return null;
453
+ out += "\0";
454
+ break;
455
+ case "x": {
456
+ const hex = raw.slice(i2, i2 + 2);
457
+ if (!/^[0-9a-fA-F]{2}$/.test(hex)) return null;
458
+ out += String.fromCharCode(Number.parseInt(hex, 16));
459
+ i2 += 2;
460
+ break;
461
+ }
462
+ case "u": {
463
+ if (raw[i2] === "{") {
464
+ const close = raw.indexOf("}", i2);
465
+ if (close === -1) return null;
466
+ const hex = raw.slice(i2 + 1, close);
467
+ if (!/^[0-9a-fA-F]+$/.test(hex)) return null;
468
+ const cp = Number.parseInt(hex, 16);
469
+ if (cp > 1114111) return null;
470
+ out += String.fromCodePoint(cp);
471
+ i2 = close + 1;
472
+ } else {
473
+ const hex = raw.slice(i2, i2 + 4);
474
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
475
+ out += String.fromCharCode(Number.parseInt(hex, 16));
476
+ i2 += 4;
477
+ }
478
+ break;
479
+ }
480
+ case "\r":
481
+ if (raw[i2] === "\n") i2++;
482
+ break;
483
+ case "\n":
484
+ case "\u2028":
485
+ case "\u2029":
486
+ break;
487
+ case void 0:
488
+ return null;
489
+ default:
490
+ if (isDigit(e.charCodeAt(0))) return null;
491
+ out += e;
492
+ }
493
+ }
494
+ return out;
495
+ }
496
+ function stringTokenValue(code2, tok) {
497
+ if (tok.type !== "string") return null;
498
+ return cookEscapes(code2.slice(tok.start + 1, tok.end - 1));
499
+ }
500
+ function collectImports(code2, tokens) {
501
+ const decls = [];
502
+ for (let i2 = 0; i2 < tokens.length; i2++) {
503
+ const tok = tokens[i2];
504
+ if (tok.type !== "ident" || tok.value !== "import") continue;
505
+ const prev = tokens[i2 - 1];
506
+ if (prev && prev.type === "punct" && (prev.value === "." || prev.value === "?.")) continue;
507
+ const next = tokens[i2 + 1];
508
+ if (!next) return null;
509
+ if (next.type === "punct" && (next.value === "(" || next.value === ".")) continue;
510
+ let j = i2 + 1;
511
+ const bindings = [];
512
+ let typeOnly = false;
513
+ if (next.type === "string") {
514
+ const source3 = stringTokenValue(code2, next);
515
+ if (source3 === null) return null;
516
+ let end2 = next.end;
517
+ const semi = tokens[j + 1];
518
+ if (semi && semi.type === "punct" && semi.value === ";") end2 = semi.end;
519
+ decls.push({ source: source3, bindings, start: tok.start, end: end2 });
520
+ continue;
521
+ }
522
+ const at = (k) => tokens[k];
523
+ const isPunct = (k, v) => {
524
+ const t = at(k);
525
+ return !!t && t.type === "punct" && t.value === v;
526
+ };
527
+ const isIdent = (k, v) => {
528
+ const t = at(k);
529
+ return !!t && t.type === "ident" && (v === void 0 || t.value === v);
530
+ };
531
+ if (isIdent(j, "type") && !isIdent(j + 1, "from") && !isPunct(j + 1, ",")) {
532
+ typeOnly = true;
533
+ j++;
534
+ }
535
+ if (isIdent(j) && !isIdent(j, "from")) {
536
+ bindings.push({ imported: "default", local: at(j).value });
537
+ j++;
538
+ if (isPunct(j, ",")) j++;
539
+ }
540
+ if (isPunct(j, "*")) {
541
+ if (!isIdent(j + 1, "as") || !isIdent(j + 2)) return null;
542
+ bindings.push({ imported: "*", local: at(j + 2).value });
543
+ j += 3;
544
+ } else if (isPunct(j, "{")) {
545
+ j++;
546
+ while (!isPunct(j, "}")) {
547
+ const t = at(j);
548
+ if (!t) return null;
549
+ let specTypeOnly = false;
550
+ if (isIdent(j, "type") && (isIdent(j + 1) || at(j + 1)?.type === "string") && !isIdent(j + 1, "as")) {
551
+ specTypeOnly = true;
552
+ j++;
553
+ }
554
+ const nameTok = at(j);
555
+ let imported = null;
556
+ if (nameTok?.type === "ident") imported = nameTok.value;
557
+ else if (nameTok?.type === "string") imported = stringTokenValue(code2, nameTok);
558
+ if (imported === null) return null;
559
+ j++;
560
+ let local = imported;
561
+ if (isIdent(j, "as")) {
562
+ if (!isIdent(j + 1)) return null;
563
+ local = at(j + 1).value;
564
+ j += 2;
565
+ }
566
+ if (!specTypeOnly) bindings.push({ imported, local });
567
+ if (isPunct(j, ",")) j++;
568
+ else if (!isPunct(j, "}")) return null;
569
+ }
570
+ j++;
571
+ }
572
+ if (!isIdent(j, "from")) return null;
573
+ const srcTok = at(j + 1);
574
+ if (!srcTok || srcTok.type !== "string") return null;
575
+ const source2 = stringTokenValue(code2, srcTok);
576
+ if (source2 === null) return null;
577
+ let end = srcTok.end;
578
+ j += 2;
579
+ if (isIdent(j, "with") || isIdent(j, "assert")) {
580
+ if (!isPunct(j + 1, "{")) return null;
581
+ let k = j + 2;
582
+ while (at(k) && !isPunct(k, "}")) k++;
583
+ if (!at(k)) return null;
584
+ end = at(k).end;
585
+ j = k + 1;
586
+ }
587
+ if (isPunct(j, ";")) end = at(j).end;
588
+ decls.push({ source: source2, bindings: typeOnly ? [] : bindings, start: tok.start, end });
589
+ i2 = j;
590
+ }
591
+ return decls;
592
+ }
593
+ var SCAN_CACHE_SIZE = 4;
594
+ var scanCache = /* @__PURE__ */ new Map();
595
+ function scanModule(code2) {
596
+ const hit = scanCache.get(code2);
597
+ if (hit !== void 0) return hit;
598
+ const tokens = tokenize(code2);
599
+ const imports = tokens && collectImports(code2, tokens);
600
+ const scan = tokens && imports ? { tokens, imports } : null;
601
+ if (scanCache.size >= SCAN_CACHE_SIZE) scanCache.delete(scanCache.keys().next().value);
602
+ scanCache.set(code2, scan);
603
+ return scan;
604
+ }
605
+ function isSibuSource(source2) {
606
+ return source2 === "sibujs" || source2.startsWith("sibujs/");
607
+ }
608
+ function sibuBindings(imports, mainOnly = false) {
609
+ const out = /* @__PURE__ */ new Map();
610
+ for (const decl of imports) {
611
+ if (mainOnly ? decl.source !== "sibujs" : !isSibuSource(decl.source)) continue;
612
+ for (const b2 of decl.bindings) {
613
+ if (b2.imported === "*" || b2.imported === "default") continue;
614
+ out.set(b2.local, b2.imported);
615
+ }
616
+ }
617
+ return out;
618
+ }
619
+ function forEachBindingRef(tokens, imports, visit) {
620
+ const inImport = (offset) => imports.some((d) => offset >= d.start && offset < d.end);
621
+ const walk = (list) => {
622
+ for (let i2 = 0; i2 < list.length; i2++) {
623
+ const tok = list[i2];
624
+ if (tok.type === "template") {
625
+ for (const e of tok.exprs) walk(e.tokens);
626
+ continue;
627
+ }
628
+ if (tok.type !== "ident") continue;
629
+ const prev = list[i2 - 1];
630
+ if (prev && prev.type === "punct" && (prev.value === "." || prev.value === "?." || prev.value === "#")) continue;
631
+ if (inImport(tok.start)) continue;
632
+ visit({ tok, list, index: i2 });
633
+ }
634
+ };
635
+ walk(tokens);
636
+ }
637
+ var EXPRESSION_KEYWORDS = /* @__PURE__ */ new Set([...REGEX_AFTER_KEYWORD, "default", "extends"]);
638
+ function isLineTerminator(c) {
639
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
640
+ }
641
+ function lineBreakBetween(code2, from, to) {
642
+ for (let i2 = from; i2 < to; i2++) if (isLineTerminator(code2.charCodeAt(i2))) return true;
643
+ return false;
644
+ }
645
+ function endsValue(tok) {
646
+ if (!tok) return false;
647
+ switch (tok.type) {
648
+ case "number":
649
+ case "string":
650
+ case "regex":
651
+ case "template":
652
+ return true;
653
+ case "ident":
654
+ return !EXPRESSION_KEYWORDS.has(tok.value);
655
+ case "punct":
656
+ return tok.value === ")" || tok.value === "]";
657
+ }
658
+ }
659
+ function isDeclarationHead(code2, list, index) {
660
+ const tok = list[index];
661
+ const prev = list[index - 1];
662
+ if (prev?.type === "ident") {
663
+ if (prev.value === "function") return true;
664
+ if (!EXPRESSION_KEYWORDS.has(prev.value) && !lineBreakBetween(code2, prev.end, tok.start)) return true;
665
+ } else if (prev?.type === "punct") {
666
+ const before = list[index - 2];
667
+ if (prev.value === "*" && !endsValue(before) && !(before?.type === "ident" && before.value === "yield"))
668
+ return true;
669
+ if (prev.value === "@") return true;
670
+ }
671
+ let depth = 0;
672
+ let close = -1;
673
+ for (let k = index + 1; k < list.length; k++) {
674
+ const t = list[k];
675
+ if (t.type !== "punct") continue;
676
+ if (t.value === "(") depth++;
677
+ else if (t.value === ")" && --depth === 0) {
678
+ close = k;
679
+ break;
680
+ }
681
+ }
682
+ if (close === -1) return true;
683
+ const after = list[close + 1];
684
+ if (after?.type !== "punct") return false;
685
+ if (after.value === "{") return true;
686
+ if (after.value === ":") {
687
+ if (!prev) return true;
688
+ if (prev.type === "punct") return ["{", "}", ";", ",", "*"].includes(prev.value);
689
+ return prev.type === "ident" && !EXPRESSION_KEYWORDS.has(prev.value);
690
+ }
691
+ return false;
692
+ }
693
+ function bindingUses(code2, scan, names) {
694
+ const shadowed = /* @__PURE__ */ new Set();
695
+ const bound = /* @__PURE__ */ new Map();
696
+ for (const decl of scan.imports) {
697
+ for (const b2 of decl.bindings) bound.set(b2.local, (bound.get(b2.local) ?? 0) + 1);
698
+ }
699
+ for (const name of names) if ((bound.get(name) ?? 0) > 1) shadowed.add(name);
700
+ const calls = [];
701
+ const tagged = [];
702
+ forEachBindingRef(scan.tokens, scan.imports, (ref2) => {
703
+ const { tok, list, index } = ref2;
704
+ if (!names.has(tok.value)) return;
705
+ const next = list[index + 1];
706
+ if (next && next.type === "punct" && next.value === "(") {
707
+ if (isDeclarationHead(code2, list, index)) shadowed.add(tok.value);
708
+ else calls.push(ref2);
709
+ } else if (next && next.type === "template") {
710
+ const prev = list[index - 1];
711
+ if (prev?.type === "punct" && prev.value === "@") shadowed.add(tok.value);
712
+ else tagged.push(ref2);
713
+ } else {
714
+ shadowed.add(tok.value);
715
+ }
716
+ });
717
+ return {
718
+ calls: calls.filter((r) => !shadowed.has(r.tok.value)),
719
+ tagged: tagged.filter((r) => !shadowed.has(r.tok.value)),
720
+ shadowed
721
+ };
722
+ }
723
+ function uniquePrefix(code2, base2 = "__sibujs") {
724
+ let prefix = `${base2}$`;
725
+ for (let n = 1; code2.includes(prefix); n++) prefix = `${base2}${n}$`;
726
+ return prefix;
727
+ }
728
+ var PURE_FACTORIES = /* @__PURE__ */ new Set([
729
+ "tagFactory",
730
+ "context",
731
+ "defineComponent",
732
+ "withProps",
733
+ "withDefaults",
734
+ "pure",
735
+ "noSideEffect"
736
+ ]);
737
+ var PURE_COMMENT = "/*#__PURE__*/ ";
738
+ function pureAnnotationEdits(code2) {
739
+ if (!code2.includes("sibujs")) return [];
740
+ const scan = scanModule(code2);
741
+ if (!scan) return [];
742
+ const candidates = /* @__PURE__ */ new Set();
743
+ for (const [local, imported] of sibuBindings(scan.imports)) if (PURE_FACTORIES.has(imported)) candidates.add(local);
744
+ if (candidates.size === 0) return [];
745
+ const uses = bindingUses(code2, scan, candidates);
746
+ const unsafe = new Set(uses.shadowed);
747
+ for (const ref2 of uses.tagged) unsafe.add(ref2.tok.value);
748
+ return uses.calls.filter(({ tok }) => {
749
+ if (unsafe.has(tok.value)) return false;
750
+ return !/\/\*\s*[#@]__PURE__\s*\*\/\s*$/.test(code2.slice(Math.max(0, tok.start - 40), tok.start));
751
+ }).map(({ tok }) => ({ start: tok.start, end: tok.start, text: PURE_COMMENT }));
752
+ }
753
+ function injectPureAnnotations(code2) {
754
+ const edits = normalizeEdits(pureAnnotationEdits(code2));
755
+ return edits && edits.length > 0 ? applyEdits(code2, edits) : code2;
756
+ }
757
+ function importsSibu(code2) {
758
+ if (!code2.includes("sibujs")) return false;
759
+ const scan = scanModule(code2);
760
+ return !!scan?.imports.some((d) => isSibuSource(d.source));
761
+ }
762
+
763
+ // src/core/dev.ts
764
+ var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
765
+ // safe default: off in browser, on in test/dev Node
766
+ typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
767
+ );
768
+ function isDev() {
769
+ if (typeof globalThis.__SIBU_DEV__ !== "undefined") return !!globalThis.__SIBU_DEV__;
770
+ if (typeof __SIBU_DEV__ !== "undefined") return !!__SIBU_DEV__;
771
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
772
+ }
773
+ function devAssert(condition, message) {
774
+ if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
775
+ throw new Error(`[SibuJS] ${message}`);
776
+ }
777
+ }
778
+ function devWarnLazy(build) {
779
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
780
+ const message = build();
781
+ if (message) console.warn(`[SibuJS] ${message}`);
782
+ }
783
+ }
784
+ function devWarn(message) {
785
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
786
+ console.warn(`[SibuJS] ${message}`);
787
+ }
788
+ }
789
+
790
+ // src/utils/sanitize.ts
791
+ function stripControlChars(value) {
792
+ return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
793
+ }
794
+ function isEventHandlerAttr(name) {
795
+ if (name.length < 3) return false;
796
+ const lower = name.toLowerCase();
797
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
798
+ }
799
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
800
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
801
+ function sanitizeUrl(url) {
802
+ const probe = stripControlChars(url).trim();
803
+ if (!probe) return "";
804
+ const output2 = url.replace(OUTER_TRIM, "");
805
+ const lower = probe.toLowerCase();
806
+ let schemeEnd = -1;
807
+ for (let i2 = 0; i2 < lower.length; i2++) {
808
+ const ch = lower.charCodeAt(i2);
809
+ if (ch === 58) {
810
+ schemeEnd = i2;
811
+ break;
812
+ }
813
+ if (ch === 47 || ch === 63 || ch === 35) break;
814
+ }
815
+ if (schemeEnd === -1) return output2;
816
+ const scheme = lower.slice(0, schemeEnd + 1);
817
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
818
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
819
+ return output2;
820
+ }
821
+ var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
822
+ function sanitizeSrcset(value) {
823
+ const parts = value.split(",");
824
+ const out = [];
825
+ for (let i2 = 0; i2 < parts.length; i2++) {
826
+ const part = parts[i2].trim();
827
+ if (!part) continue;
828
+ const m = part.match(/^(\S+)(\s+.+)?$/);
829
+ if (!m) continue;
830
+ if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
831
+ const safe = sanitizeUrl(m[1]);
832
+ if (!safe) continue;
833
+ out.push(m[2] ? `${safe}${m[2]}` : safe);
834
+ }
835
+ return out.join(", ");
836
+ }
837
+ var CSS_DANGER_GATE = /[(:@\\]/;
838
+ function isCssWhitespace(ch) {
839
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
840
+ }
841
+ function isHexDigit(ch) {
842
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
843
+ }
844
+ function decodeCssEscapes(value) {
845
+ let out = "";
846
+ for (let i2 = 0; i2 < value.length; i2++) {
847
+ const ch = value[i2];
848
+ if (ch !== "\\") {
849
+ out += ch;
850
+ continue;
851
+ }
852
+ if (i2 + 1 >= value.length) break;
853
+ const next = value[i2 + 1];
854
+ if (next === "\n" || next === "\f") {
855
+ i2 += 1;
856
+ continue;
857
+ }
858
+ if (next === "\r") {
859
+ i2 += value[i2 + 2] === "\n" ? 2 : 1;
860
+ continue;
861
+ }
862
+ if (isHexDigit(next)) {
863
+ let hex = "";
864
+ let j = i2 + 1;
865
+ while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
866
+ hex += value[j];
867
+ j++;
868
+ }
869
+ if (j < value.length && isCssWhitespace(value[j])) {
870
+ j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
871
+ }
872
+ const code2 = Number.parseInt(hex, 16);
873
+ const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
874
+ out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
875
+ i2 = j - 1;
876
+ continue;
877
+ }
878
+ out += next;
879
+ i2 += 1;
880
+ }
881
+ return out;
882
+ }
883
+ var BLOCKED_CSS_NEEDLES = [
884
+ "url(",
885
+ "expression(",
886
+ "javascript:",
887
+ "vbscript:",
888
+ "-moz-binding",
889
+ "behavior:",
890
+ "@import",
891
+ "image-set(",
892
+ "filter:progid"
893
+ ];
894
+ function describeElement(el) {
895
+ if (!el) return "an element";
896
+ let out = el.tagName.toLowerCase();
897
+ const id = el.getAttribute("id");
898
+ if (id) out += `#${id}`;
899
+ const cls = el.getAttribute("class");
900
+ if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
901
+ return `<${out}>`;
902
+ }
903
+ var warnedDeclarations = /* @__PURE__ */ new Set();
904
+ var MAX_WARNED_DECLARATIONS = 100;
905
+ var declarationCapAnnounced = false;
906
+ function warnDroppedDeclaration(value, reason, ctx) {
907
+ devWarnLazy(() => {
908
+ const reasons = [
909
+ "url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
910
+ "expression() executes arbitrary script in legacy engines",
911
+ "the javascript: scheme executes script",
912
+ "the vbscript: scheme executes script",
913
+ "-moz-binding loads and runs remote XBL",
914
+ "behavior: loads and runs a remote HTC script",
915
+ "@import pulls in a remote stylesheet",
916
+ "image-set() fetches remote images, the same exfiltration channel as url()",
917
+ "filter:progid activates legacy scriptable filters"
918
+ ];
919
+ const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
920
+ const where = describeElement(ctx?.element);
921
+ const key = `${where}|${declaration}`;
922
+ if (warnedDeclarations.has(key)) return "";
923
+ if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
924
+ if (declarationCapAnnounced) return "";
925
+ declarationCapAnnounced = true;
926
+ return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
927
+ }
928
+ warnedDeclarations.add(key);
929
+ return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
930
+ });
931
+ }
932
+ function blockedCssReason(value) {
933
+ if (!CSS_DANGER_GATE.test(value)) return -1;
934
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
935
+ const lower = normalized.toLowerCase().replace(/\s+/g, "");
936
+ for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
937
+ if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
938
+ }
939
+ return -1;
940
+ }
941
+ function sanitizeCSSValue(value, context2) {
942
+ const reason = blockedCssReason(value);
943
+ if (reason === -1) return value;
944
+ warnDroppedDeclaration(value, reason, context2);
945
+ return "";
946
+ }
947
+ function sanitizeStyleAttribute(cssText, context2) {
948
+ const input2 = String(cssText);
949
+ if (input2.trim() === "") return "";
950
+ if (typeof document === "undefined") {
951
+ return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
952
+ }
953
+ const probe = document.createElement("div");
954
+ try {
955
+ probe.style.cssText = input2;
956
+ } catch {
957
+ return "";
958
+ }
959
+ const declarations = [];
960
+ for (let i2 = 0; i2 < probe.style.length; i2++) {
961
+ const property = probe.style[i2];
962
+ const value = probe.style.getPropertyValue(property);
963
+ let reason = blockedCssReason(value);
964
+ if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
965
+ if (reason !== -1) {
966
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
967
+ continue;
968
+ }
969
+ const priority = probe.style.getPropertyPriority(property);
970
+ declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
971
+ }
972
+ return declarations.join("; ");
973
+ }
974
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
975
+ "href",
976
+ "xlink:href",
977
+ "src",
978
+ "action",
979
+ "formaction",
980
+ "formtarget",
981
+ "cite",
982
+ "poster",
983
+ "background",
984
+ "srcset",
985
+ "ping",
986
+ "data",
987
+ // `<html manifest>` is a URL sink too. It was carried only by the SSR
988
+ // serializers' private copy of this set; folding that copy into this one would
989
+ // otherwise have silently dropped its coverage.
990
+ "manifest"
991
+ ]);
992
+ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
993
+ function isHtmlContentAttribute(name) {
994
+ return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
995
+ }
996
+ function isUrlAttribute(attr) {
997
+ return URL_ATTRIBUTES.has(attr.toLowerCase());
998
+ }
999
+ function sanitizeAttributeString(attr, value, context2) {
1000
+ const lower = attr.toLowerCase();
1001
+ if (lower === "srcset") return sanitizeSrcset(value);
1002
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1003
+ if (lower === "style") return sanitizeStyleAttribute(value, context2);
1004
+ return value;
1005
+ }
1006
+
1007
+ // src/build/staticAnalysis.ts
1008
+ var STATIC_TAGS = /* @__PURE__ */ new Set([
1009
+ "div",
1010
+ "span",
1011
+ "section",
1012
+ "article",
1013
+ "header",
1014
+ "footer",
1015
+ "nav",
1016
+ "main",
1017
+ "aside",
1018
+ "p",
1019
+ "h1",
1020
+ "h2",
1021
+ "h3",
1022
+ "h4",
1023
+ "h5",
1024
+ "h6",
1025
+ "blockquote",
1026
+ "li",
1027
+ "ol",
1028
+ "ul",
1029
+ "a",
1030
+ "b",
1031
+ "em",
1032
+ "i",
1033
+ "strong",
1034
+ "small",
1035
+ "code",
1036
+ "mark",
1037
+ "img",
1038
+ "br",
1039
+ "hr",
1040
+ "input",
1041
+ "button",
1042
+ "form",
1043
+ "label",
1044
+ "details",
1045
+ "summary",
1046
+ "dialog"
1047
+ ]);
63
1048
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1049
+ "br",
1050
+ "hr",
1051
+ "img",
1052
+ "input",
1053
+ "meta",
1054
+ "link",
1055
+ "area",
1056
+ "base",
1057
+ "col",
1058
+ "embed",
1059
+ "param",
1060
+ "source",
1061
+ "track",
1062
+ "wbr"
1063
+ ]);
1064
+ var SPECIAL_PROPS = /* @__PURE__ */ new Set(["style", "ref", "on", "onElement"]);
1065
+ var NON_REFLECTING_BOOLEANS = /* @__PURE__ */ new Set(["checked", "selected"]);
1066
+ function readLiteral(code2, tok) {
1067
+ if (!tok) return null;
1068
+ if (tok.type === "string") {
1069
+ const value = stringTokenValue(code2, tok);
1070
+ return value === null ? null : { kind: "string", value };
1071
+ }
1072
+ if (tok.type === "number" && /^(0|[1-9]\d*)(\.\d+)?$/.test(tok.value)) {
1073
+ return { kind: "number", value: String(Number(tok.value)) };
1074
+ }
1075
+ if (tok.type === "ident" && ["true", "false", "null", "undefined"].includes(tok.value)) {
1076
+ return { kind: "keyword", value: tok.value };
1077
+ }
1078
+ return null;
1079
+ }
1080
+ function parserSafeText(s2) {
1081
+ return !s2.includes("\r") && !s2.includes("\0");
1082
+ }
1083
+ function escapeHtml(str) {
1084
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1085
+ }
1086
+ function escapeAttr(str) {
1087
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1088
+ }
1089
+ function attributeHtml(name, lit) {
1090
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) return null;
1091
+ const lower = name.toLowerCase();
1092
+ if (SPECIAL_PROPS.has(name) || lower === "style" || lower === "srcset") return null;
1093
+ if (isEventHandlerAttr(name) || isUrlAttribute(name) || isHtmlContentAttribute(name)) return null;
1094
+ if (lit.kind === "keyword") {
1095
+ if (lit.value === "null" || lit.value === "undefined") return "";
1096
+ const on2 = lit.value === "true";
1097
+ if (lower.startsWith("aria-")) return `${lower}="${on2}"`;
1098
+ if (NON_REFLECTING_BOOLEANS.has(lower)) return null;
1099
+ return on2 ? `${lower}=""` : "";
1100
+ }
1101
+ if (!parserSafeText(lit.value)) return null;
1102
+ return `${lower}="${escapeAttr(lit.value)}"`;
1103
+ }
1104
+ function parseCall(code2, list, i2) {
1105
+ const at = (k2) => list[k2];
1106
+ const isPunct = (k2, v) => {
1107
+ const t = at(k2);
1108
+ return !!t && t.type === "punct" && t.value === v;
1109
+ };
1110
+ let k = i2 + 1;
1111
+ const result = { end: 0, cls: null, id: null, attrs: [], text: null };
1112
+ let nodesText = null;
1113
+ if (isPunct(k, "{")) {
1114
+ k++;
1115
+ const seen = /* @__PURE__ */ new Set();
1116
+ while (!isPunct(k, "}")) {
1117
+ const keyTok = at(k);
1118
+ let key = null;
1119
+ if (keyTok?.type === "ident") key = keyTok.value;
1120
+ else if (keyTok?.type === "string") key = stringTokenValue(code2, keyTok);
1121
+ if (key === null || !isPunct(k + 1, ":")) return null;
1122
+ if (seen.has(key.toLowerCase())) return null;
1123
+ seen.add(key.toLowerCase());
1124
+ const lit = readLiteral(code2, at(k + 2));
1125
+ if (lit === null) return null;
1126
+ k += 3;
1127
+ if (isPunct(k, ",")) k++;
1128
+ else if (!isPunct(k, "}")) return null;
1129
+ if (key === "class" || key === "id") {
1130
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1131
+ if (key === "class") result.cls = lit.value;
1132
+ else result.id = lit.value;
1133
+ } else if (key === "nodes") {
1134
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1135
+ nodesText = lit.value;
1136
+ } else {
1137
+ const html3 = attributeHtml(key, lit);
1138
+ if (html3 === null) return null;
1139
+ if (html3) result.attrs.push(html3);
1140
+ }
1141
+ }
1142
+ k++;
1143
+ if (isPunct(k, ",")) {
1144
+ const second = readLiteral(code2, at(k + 1));
1145
+ if (second === null) {
1146
+ if (!isPunct(k + 1, ")")) return null;
1147
+ k++;
1148
+ } else {
1149
+ if (second.kind !== "string" || !parserSafeText(second.value)) return null;
1150
+ result.text = second.value;
1151
+ k += 2;
1152
+ if (isPunct(k, ",")) k++;
1153
+ }
1154
+ }
1155
+ if (result.text === null) result.text = nodesText;
1156
+ } else {
1157
+ const first = readLiteral(code2, at(k));
1158
+ if (first === null || first.kind !== "string" || !parserSafeText(first.value)) return null;
1159
+ k++;
1160
+ if (isPunct(k, ",") && at(k + 1)?.type === "string") {
1161
+ const second = readLiteral(code2, at(k + 1));
1162
+ if (second === null || second.kind !== "string" || !parserSafeText(second.value)) return null;
1163
+ result.cls = first.value;
1164
+ result.text = second.value;
1165
+ k += 2;
1166
+ } else {
1167
+ result.text = first.value;
1168
+ }
1169
+ if (isPunct(k, ",")) k++;
1170
+ }
1171
+ if (!isPunct(k, ")")) return null;
1172
+ result.end = at(k).end;
1173
+ return result;
1174
+ }
1175
+ function toHtml(tag, call) {
1176
+ const attrs = [];
1177
+ if (call.cls !== null) attrs.push(`class="${escapeAttr(call.cls)}"`);
1178
+ if (call.id !== null) attrs.push(`id="${escapeAttr(call.id)}"`);
1179
+ attrs.push(...call.attrs);
1180
+ const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
1181
+ if (VOID_ELEMENTS.has(tag)) {
1182
+ if (call.text) return null;
1183
+ return `<${tag}${attrStr} />`;
1184
+ }
1185
+ return `<${tag}${attrStr}>${escapeHtml(call.text ?? "")}</${tag}>`;
1186
+ }
1187
+ function analyzeStaticTemplates(code2) {
1188
+ const none = { hasStaticPatterns: false, patterns: [] };
1189
+ if (!code2.includes("sibujs")) return none;
1190
+ const scan = scanModule(code2);
1191
+ if (!scan) return none;
1192
+ const factories = /* @__PURE__ */ new Map();
1193
+ for (const decl of scan.imports) {
1194
+ if (decl.source !== "sibujs") continue;
1195
+ for (const b2 of decl.bindings) if (STATIC_TAGS.has(b2.imported)) factories.set(b2.local, b2.imported);
1196
+ }
1197
+ if (factories.size === 0) return none;
1198
+ const uses = bindingUses(code2, scan, new Set(factories.keys()));
1199
+ const unsafe = new Set(uses.shadowed);
1200
+ for (const ref2 of uses.tagged) unsafe.add(ref2.tok.value);
1201
+ const patterns = [];
1202
+ for (const { tok, list, index } of uses.calls) {
1203
+ if (unsafe.has(tok.value)) continue;
1204
+ const tag = factories.get(tok.value);
1205
+ const call = parseCall(code2, list, index + 1);
1206
+ if (!call) continue;
1207
+ const templateHtml = toHtml(tag, call);
1208
+ if (templateHtml === null) continue;
1209
+ const start = list[index].start;
1210
+ patterns.push({ original: code2.slice(start, call.end), tag, templateHtml, start, end: call.end });
1211
+ }
1212
+ return { hasStaticPatterns: patterns.length > 0, patterns };
1213
+ }
1214
+
1215
+ // src/build/templateCompiler.ts
1216
+ var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
1217
+ var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
64
1218
  "area",
65
1219
  "base",
66
1220
  "br",
@@ -90,105 +1244,19 @@ var SVG_TAGS = /* @__PURE__ */ new Set([
90
1244
  "tspan",
91
1245
  "defs",
92
1246
  "clipPath",
93
- "mask",
94
- "pattern",
95
- "linearGradient",
96
- "radialGradient",
97
- "stop",
98
- "use",
99
- "symbol",
100
- "marker"
101
- ]);
102
- function findHtmlTemplates(code2) {
103
- const results = [];
104
- const tagRegex = /\bhtml\s*`/g;
105
- let match2;
106
- while ((match2 = tagRegex.exec(code2)) !== null) {
107
- const start = match2.index;
108
- const backtickStart = code2.indexOf("`", start + 4);
109
- if (backtickStart === -1) continue;
110
- const parsed = parseTemplateLiteral(code2, backtickStart);
111
- if (!parsed) continue;
112
- results.push({
113
- start,
114
- end: parsed.end,
115
- strings: parsed.strings,
116
- exprCount: parsed.exprCount
117
- });
118
- tagRegex.lastIndex = parsed.end;
119
- }
120
- return results;
121
- }
122
- function parseTemplateLiteral(code2, openBacktick) {
123
- const strings = [];
124
- let exprCount = 0;
125
- let pos = openBacktick + 1;
126
- let current = "";
127
- while (pos < code2.length) {
128
- const ch = code2[pos];
129
- if (ch === "`") {
130
- strings.push(current);
131
- return { end: pos + 1, strings, exprCount };
132
- }
133
- if (ch === "$" && pos + 1 < code2.length && code2[pos + 1] === "{") {
134
- strings.push(current);
135
- current = "";
136
- exprCount++;
137
- pos += 2;
138
- let depth = 1;
139
- while (pos < code2.length && depth > 0) {
140
- const c = code2[pos];
141
- if (c === "{") depth++;
142
- else if (c === "}") depth--;
143
- else if (c === '"' || c === "'" || c === "`") {
144
- pos = skipString(code2, pos);
145
- continue;
146
- }
147
- if (depth > 0) pos++;
148
- }
149
- if (depth !== 0) return null;
150
- pos++;
151
- continue;
152
- }
153
- if (ch === "\\") {
154
- current += code2[pos] + (code2[pos + 1] || "");
155
- pos += 2;
156
- continue;
157
- }
158
- current += ch;
159
- pos++;
160
- }
161
- return null;
162
- }
163
- function skipString(code2, start) {
164
- const quote2 = code2[start];
165
- let pos = start + 1;
166
- while (pos < code2.length) {
167
- if (code2[pos] === "\\") {
168
- pos += 2;
169
- continue;
170
- }
171
- if (code2[pos] === quote2) return pos + 1;
172
- if (quote2 === "`" && code2[pos] === "$" && code2[pos + 1] === "{") {
173
- pos += 2;
174
- let depth = 1;
175
- while (pos < code2.length && depth > 0) {
176
- if (code2[pos] === "{") depth++;
177
- else if (code2[pos] === "}") depth--;
178
- else if (code2[pos] === '"' || code2[pos] === "'" || code2[pos] === "`") {
179
- pos = skipString(code2, pos);
180
- continue;
181
- }
182
- if (depth > 0) pos++;
183
- }
184
- pos++;
185
- continue;
186
- }
187
- pos++;
188
- }
189
- return pos;
190
- }
191
- function parseTemplateToAST(strings) {
1247
+ "mask",
1248
+ "pattern",
1249
+ "linearGradient",
1250
+ "radialGradient",
1251
+ "stop",
1252
+ "use",
1253
+ "symbol",
1254
+ "marker"
1255
+ ]);
1256
+ var SVG_NS = "http://www.w3.org/2000/svg";
1257
+ var RuntimeParseError = class extends Error {
1258
+ };
1259
+ function parseTemplate(strings) {
192
1260
  const exprCount = strings.length - 1;
193
1261
  let template2 = strings[0];
194
1262
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -197,26 +1265,34 @@ function parseTemplateToAST(strings) {
197
1265
  let pos = 0;
198
1266
  const len = template2.length;
199
1267
  function skipWs() {
200
- while (pos < len && /\s/.test(template2[pos])) pos++;
1268
+ while (pos < len && (template2[pos] === " " || template2[pos] === " " || template2[pos] === "\n" || template2[pos] === "\r"))
1269
+ pos++;
201
1270
  }
202
1271
  function tryExprIdx() {
203
- if (template2[pos] !== "\0") return -1;
1272
+ if (template2.charCodeAt(pos) !== 0) return -1;
204
1273
  const start = pos;
205
1274
  pos++;
206
- let idxStr = "";
207
- while (pos < len && template2[pos] !== "\0") idxStr += template2[pos++];
1275
+ let idx = 0;
1276
+ while (pos < len && template2.charCodeAt(pos) !== 0) {
1277
+ idx = idx * 10 + (template2.charCodeAt(pos) - 48);
1278
+ pos++;
1279
+ }
208
1280
  if (pos < len) pos++;
209
- const idx = Number.parseInt(idxStr, 10);
210
- if (Number.isNaN(idx) || idx < 0 || idx >= exprCount) {
1281
+ if (idx < 0 || idx >= exprCount) {
211
1282
  pos = start;
212
1283
  return -1;
213
1284
  }
214
1285
  return idx;
215
1286
  }
216
1287
  function readTagName() {
217
- let name = "";
218
- while (pos < len && /[a-zA-Z0-9-]/.test(template2[pos])) name += template2[pos++];
219
- return name;
1288
+ const start = pos;
1289
+ while (pos < len) {
1290
+ const c = template2.charCodeAt(pos);
1291
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45) {
1292
+ pos++;
1293
+ } else break;
1294
+ }
1295
+ return template2.slice(start, pos);
220
1296
  }
221
1297
  function parseAttrValue() {
222
1298
  skipWs();
@@ -224,51 +1300,72 @@ function parseTemplateToAST(strings) {
224
1300
  pos++;
225
1301
  skipWs();
226
1302
  const exprIdx = tryExprIdx();
227
- if (exprIdx >= 0) return { kind: "expr", index: exprIdx };
1303
+ if (exprIdx >= 0) return { kind: "expr", idx: exprIdx };
228
1304
  const quote2 = template2[pos];
229
1305
  if (quote2 === '"' || quote2 === "'") {
230
1306
  pos++;
231
- const parts = [];
232
- let current = "";
1307
+ const statics2 = [];
1308
+ const exprs2 = [];
1309
+ let current2 = "";
233
1310
  while (pos < len && template2[pos] !== quote2) {
234
1311
  const innerIdx = tryExprIdx();
235
1312
  if (innerIdx >= 0) {
236
- if (current) parts.push({ kind: "static", value: current });
237
- current = "";
238
- parts.push({ kind: "expr", index: innerIdx });
1313
+ statics2.push(current2);
1314
+ current2 = "";
1315
+ exprs2.push(innerIdx);
239
1316
  } else {
240
- current += template2[pos++];
1317
+ current2 += template2[pos++];
241
1318
  }
242
1319
  }
243
1320
  if (pos < len) pos++;
244
- if (current) parts.push({ kind: "static", value: current });
245
- if (parts.length === 1 && parts[0].kind === "static") {
246
- return { kind: "static", value: parts[0].value };
247
- }
248
- if (parts.some((p2) => p2.kind === "expr")) {
249
- return { kind: "mixed", parts };
1321
+ statics2.push(current2);
1322
+ if (exprs2.length === 0) return { kind: "static", value: statics2[0] };
1323
+ return { kind: "mixed", statics: statics2, exprs: exprs2 };
1324
+ }
1325
+ const statics = [];
1326
+ const exprs = [];
1327
+ let current = "";
1328
+ while (pos < len) {
1329
+ const c = template2.charCodeAt(pos);
1330
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 62) break;
1331
+ const innerIdx = tryExprIdx();
1332
+ if (innerIdx >= 0) {
1333
+ statics.push(current);
1334
+ current = "";
1335
+ exprs.push(innerIdx);
1336
+ } else {
1337
+ current += template2[pos++];
250
1338
  }
251
- return { kind: "static", value: parts.map((p2) => p2.value).join("") };
252
1339
  }
253
- let val = "";
254
- while (pos < len && !/[\s>/]/.test(template2[pos])) val += template2[pos++];
255
- return { kind: "static", value: val };
1340
+ statics.push(current);
1341
+ if (exprs.length === 0) return { kind: "static", value: statics[0] };
1342
+ return { kind: "mixed", statics, exprs };
256
1343
  }
257
1344
  function parseAttrs() {
258
1345
  const attrs = [];
259
1346
  while (pos < len) {
260
1347
  skipWs();
261
1348
  if (template2[pos] === ">" || template2[pos] === "/") break;
262
- let attrName = "";
263
- while (pos < len && /[a-zA-Z0-9\-:_.]/.test(template2[pos])) attrName += template2[pos++];
1349
+ const attrStart = pos;
1350
+ while (pos < len) {
1351
+ const c = template2.charCodeAt(pos);
1352
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45 || c === 58 || c === 95 || c === 46) {
1353
+ pos++;
1354
+ } else break;
1355
+ }
1356
+ const attrName = template2.slice(attrStart, pos);
264
1357
  if (!attrName) break;
265
1358
  const val = parseAttrValue();
266
1359
  if (attrName.startsWith("on:")) {
267
- if (val.kind === "expr") {
268
- attrs.push({ name: attrName.slice(3), value: { kind: "event", index: val.index } });
269
- }
1360
+ if (val.kind === "expr") attrs.push({ t: 3, name: attrName.slice(3), idx: val.idx });
1361
+ } else if (val.kind === "bool") {
1362
+ attrs.push({ t: 4, name: attrName });
1363
+ } else if (val.kind === "static") {
1364
+ attrs.push({ t: 0, name: attrName, value: val.value });
1365
+ } else if (val.kind === "expr") {
1366
+ attrs.push({ t: 1, name: attrName, idx: val.idx });
270
1367
  } else {
271
- attrs.push({ name: attrName, value: val });
1368
+ attrs.push({ t: 2, name: attrName, statics: val.statics, exprs: val.exprs });
272
1369
  }
273
1370
  }
274
1371
  return attrs;
@@ -282,40 +1379,67 @@ function parseTemplateToAST(strings) {
282
1379
  const idx = tryExprIdx();
283
1380
  if (idx >= 0) {
284
1381
  const collapsed2 = collapseWs(text2);
285
- if (collapsed2) children.push({ kind: "text", value: collapsed2 });
1382
+ if (collapsed2) children.push({ t: 1, value: collapsed2 });
286
1383
  text2 = "";
287
- children.push({ kind: "expr", index: idx });
1384
+ children.push({ t: 2, idx });
288
1385
  } else {
289
1386
  text2 += template2[pos++];
290
1387
  }
291
1388
  }
292
1389
  const collapsed = collapseWs(text2);
293
- if (collapsed) children.push({ kind: "text", value: collapsed });
1390
+ if (collapsed) children.push({ t: 1, value: collapsed });
294
1391
  }
295
1392
  function parseChildren() {
296
1393
  const children = [];
297
1394
  while (pos < len) {
298
1395
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") break;
299
1396
  if (template2[pos] === "<") {
1397
+ const next = template2[pos + 1];
1398
+ if (next === "!") {
1399
+ if (template2.startsWith("<!--", pos)) {
1400
+ const end = template2.indexOf("-->", pos + 4);
1401
+ pos = end === -1 ? len : end + 3;
1402
+ } else if (template2.startsWith("<![CDATA[", pos)) {
1403
+ const end = template2.indexOf("]]>", pos + 9);
1404
+ pos = end === -1 ? len : end + 3;
1405
+ } else {
1406
+ const end = template2.indexOf(">", pos);
1407
+ pos = end === -1 ? len : end + 1;
1408
+ }
1409
+ continue;
1410
+ }
1411
+ if (next === "?") {
1412
+ const end = template2.indexOf(">", pos);
1413
+ pos = end === -1 ? len : end + 1;
1414
+ continue;
1415
+ }
1416
+ if (!(next >= "a" && next <= "z") && !(next >= "A" && next <= "Z")) {
1417
+ children.push({ t: 1, value: "<" });
1418
+ pos++;
1419
+ continue;
1420
+ }
300
1421
  pos++;
301
1422
  const tag = readTagName();
302
1423
  const attrs = parseAttrs();
303
1424
  skipWs();
304
- const isVoid = VOID_ELEMENTS.has(tag);
1425
+ const isVoid = VOID_ELEMENTS2.has(tag);
305
1426
  const isSelfClosing = template2[pos] === "/";
306
1427
  if (isSelfClosing) pos++;
307
1428
  if (pos < len) pos++;
308
1429
  if (isVoid || isSelfClosing) {
309
- children.push({ kind: "element", el: { tag, attrs, children: [] } });
1430
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: [] } });
310
1431
  } else {
311
1432
  const inner = parseChildren();
1433
+ if (RAW_TEXT_TAGS.has(tag.toLowerCase()) && inner.some((c) => c.t === 2)) {
1434
+ throw new RuntimeParseError(tag);
1435
+ }
312
1436
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") {
313
1437
  pos += 2;
314
1438
  readTagName();
315
1439
  skipWs();
316
1440
  if (pos < len && template2[pos] === ">") pos++;
317
1441
  }
318
- children.push({ kind: "element", el: { tag, attrs, children: inner } });
1442
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: inner } });
319
1443
  }
320
1444
  } else {
321
1445
  parseTextChildren(children);
@@ -325,303 +1449,230 @@ function parseTemplateToAST(strings) {
325
1449
  }
326
1450
  return parseChildren();
327
1451
  }
328
- function generateElement(el, valuesVar) {
329
- const tag = el.tag;
330
- const isSvg = SVG_TAGS.has(tag);
331
- const parts = [];
332
- for (const attr of el.attrs) {
333
- const v = attr.value;
334
- switch (v.kind) {
335
- case "static":
336
- parts.push(`${JSON.stringify(attr.name)}: ${JSON.stringify(v.value)}`);
337
- break;
338
- case "expr":
339
- parts.push(`${JSON.stringify(attr.name)}: ${valuesVar}[${v.index}]`);
340
- break;
341
- case "mixed": {
342
- const concat = v.parts.map((p2) => p2.kind === "static" ? JSON.stringify(p2.value) : `String(${valuesVar}[${p2.index}])`).join(" + ");
343
- parts.push(`${JSON.stringify(attr.name)}: ${concat}`);
344
- break;
345
- }
346
- case "bool":
347
- parts.push(`${JSON.stringify(attr.name)}: true`);
348
- break;
349
- case "event":
350
- break;
351
- }
352
- }
353
- const events = el.attrs.filter((a2) => a2.value.kind === "event");
354
- if (events.length > 0) {
355
- const eventEntries = events.map((a2) => {
356
- const v = a2.value;
357
- return `${JSON.stringify(a2.name)}: ${valuesVar}[${v.index}]`;
358
- });
359
- parts.push(`on: { ${eventEntries.join(", ")} }`);
360
- }
361
- if (el.children.length > 0) {
362
- const childExprs = el.children.map((c) => generateChild(c, valuesVar));
363
- if (childExprs.length === 1) {
364
- parts.push(`nodes: ${childExprs[0]}`);
1452
+ var IDL_SYNCED_ATTRS = /* @__PURE__ */ new Set(["value", "checked"]);
1453
+ function generateBody(roots, h) {
1454
+ const lines = [];
1455
+ let n = 0;
1456
+ const q2 = jsStringLiteral;
1457
+ const P = h.prefix;
1458
+ function genElement(el) {
1459
+ const v = `e${n++}`;
1460
+ h.tags.add(el.tag);
1461
+ if (el.svg) h.svg = true;
1462
+ lines.push(
1463
+ el.svg ? `const ${v} = document.createElementNS(${q2(SVG_NS)}, ${q2(el.tag)});` : `const ${v} = document.createElement(${q2(el.tag)});`
1464
+ );
1465
+ for (const attr of el.attrs) {
1466
+ switch (attr.t) {
1467
+ case 0:
1468
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, ${q2(attr.value)});`);
1469
+ break;
1470
+ case 4:
1471
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, "");`);
1472
+ break;
1473
+ case 1:
1474
+ case 2: {
1475
+ if (IDL_SYNCED_ATTRS.has(attr.name.toLowerCase())) return null;
1476
+ h.used.add("attr");
1477
+ if (attr.t === 1) {
1478
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, v${attr.idx});`);
1479
+ } else {
1480
+ h.used.add("str");
1481
+ let expr = q2(attr.statics[0]);
1482
+ for (let j = 0; j < attr.exprs.length; j++) {
1483
+ expr += ` + ${P}str(v${attr.exprs[j]}) + ${q2(attr.statics[j + 1])}`;
1484
+ }
1485
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, ${expr});`);
1486
+ }
1487
+ break;
1488
+ }
1489
+ case 3:
1490
+ h.used.add("on");
1491
+ lines.push(`${P}on(${v}, ${q2(attr.name)}, v${attr.idx});`);
1492
+ break;
1493
+ }
1494
+ }
1495
+ for (const child of el.children) {
1496
+ if (child.t === 0) {
1497
+ const c = genElement(child.el);
1498
+ if (c === null) return null;
1499
+ lines.push(`${v}.appendChild(${c});`);
1500
+ } else if (child.t === 1) {
1501
+ lines.push(`${v}.appendChild(document.createTextNode(${q2(child.value)}));`);
1502
+ } else {
1503
+ h.used.add("child");
1504
+ lines.push(`${P}child(${v}, v${child.idx});`);
1505
+ }
1506
+ }
1507
+ return v;
1508
+ }
1509
+ if (roots.length === 1 && roots[0].t === 0) {
1510
+ const root = genElement(roots[0].el);
1511
+ if (root === null) return null;
1512
+ lines.push(`return ${root};`);
1513
+ return lines.join("\n ");
1514
+ }
1515
+ lines.push(`const w = document.createElement("div");`);
1516
+ for (const root of roots) {
1517
+ if (root.t === 0) {
1518
+ const c = genElement(root.el);
1519
+ if (c === null) return null;
1520
+ lines.push(`w.appendChild(${c});`);
1521
+ } else if (root.t === 1) {
1522
+ lines.push(`w.appendChild(document.createTextNode(${q2(root.value)}));`);
365
1523
  } else {
366
- parts.push(`nodes: [${childExprs.join(", ")}]`);
1524
+ return null;
367
1525
  }
368
1526
  }
369
- const propsStr = parts.length > 0 ? `{ ${parts.join(", ")} }` : void 0;
370
- if (isSvg) {
371
- return propsStr ? `__sbTagFactory(${JSON.stringify(tag)}, __sbSVG_NS)(${propsStr})` : `__sbTagFactory(${JSON.stringify(tag)}, __sbSVG_NS)({})`;
372
- }
373
- return propsStr ? `${tag}(${propsStr})` : `${tag}({})`;
1527
+ lines.push("return w;");
1528
+ return lines.join("\n ");
374
1529
  }
375
- function generateChild(child, valuesVar) {
376
- switch (child.kind) {
377
- case "element":
378
- return generateElement(child.el, valuesVar);
379
- case "text":
380
- return JSON.stringify(child.value);
381
- case "expr":
382
- return `${valuesVar}[${child.index}]`;
1530
+ function helperSource(h) {
1531
+ const P = h.prefix;
1532
+ const out = [];
1533
+ if (h.used.has("attr")) {
1534
+ out.push(
1535
+ `function ${P}attr(el, name, value) {
1536
+ const teardown = ${P}bindAttrs(el, { [name]: value });
1537
+ if (typeof value === "function") ${P}registerDisposer(el, teardown);
1538
+ }`
1539
+ );
383
1540
  }
384
- }
385
- function compileHtmlTemplates(code2) {
386
- const templates = findHtmlTemplates(code2);
387
- if (templates.length === 0) {
388
- return { code: null, usedTags: /* @__PURE__ */ new Set(), usesSvg: false, compiledCount: 0 };
389
- }
390
- const usedTags = /* @__PURE__ */ new Set();
391
- let usesSvg = false;
392
- let result = code2;
393
- for (let i2 = templates.length - 1; i2 >= 0; i2--) {
394
- const tmpl = templates[i2];
395
- const ast = parseTemplateToAST(tmpl.strings);
396
- collectTags(ast, usedTags);
397
- if (ast.length > 1) usedTags.add("div");
398
- if (Array.from(usedTags).some((t) => SVG_TAGS.has(t))) usesSvg = true;
399
- const valuesVar = "__v";
400
- const childExprs = ast.map((c) => generateChild(c, valuesVar));
401
- const rootExpr = childExprs.length === 1 ? childExprs[0] : `div([${childExprs.join(", ")}])`;
402
- let compiled;
403
- if (tmpl.exprCount === 0) {
404
- compiled = rootExpr;
405
- } else {
406
- const body2 = rootExpr;
407
- const exprSource = extractExpressions(code2, tmpl.start, tmpl.end, tmpl.exprCount);
408
- compiled = `((${valuesVar}) => ${body2})([${exprSource.join(", ")}])`;
409
- }
410
- result = result.slice(0, tmpl.start) + compiled + result.slice(tmpl.end);
1541
+ if (h.used.has("str")) {
1542
+ out.push(`function ${P}str(value) {
1543
+ return value == null ? "" : String(value);
1544
+ }`);
411
1545
  }
412
- return { code: result, usedTags, usesSvg, compiledCount: templates.length };
413
- }
414
- function collectTags(children, tags) {
415
- for (const child of children) {
416
- if (child.kind === "element") {
417
- tags.add(child.el.tag);
418
- collectTags(child.el.children, tags);
419
- }
1546
+ if (h.used.has("on")) {
1547
+ out.push(
1548
+ `function ${P}on(el, name, handler) {
1549
+ if (typeof handler === "function") el.addEventListener(name, handler);
1550
+ }`
1551
+ );
420
1552
  }
421
- }
422
- function extractExpressions(code2, templateStart, templateEnd, count) {
423
- const exprs = [];
424
- const backtickStart = code2.indexOf("`", templateStart + 4);
425
- let pos = backtickStart + 1;
426
- for (let i2 = 0; i2 < count; i2++) {
427
- while (pos < templateEnd) {
428
- if (code2[pos] === "$" && code2[pos + 1] === "{") {
429
- pos += 2;
430
- break;
431
- }
432
- if (code2[pos] === "\\") {
433
- pos += 2;
434
- continue;
435
- }
436
- pos++;
437
- }
438
- const exprStart = pos;
439
- let depth = 1;
440
- while (pos < templateEnd && depth > 0) {
441
- const c = code2[pos];
442
- if (c === "{") depth++;
443
- else if (c === "}") {
444
- depth--;
445
- if (depth === 0) break;
446
- } else if (c === '"' || c === "'" || c === "`") {
447
- pos = skipString(code2, pos);
448
- continue;
449
- }
450
- pos++;
451
- }
452
- exprs.push(code2.slice(exprStart, pos).trim());
453
- pos++;
1553
+ if (h.used.has("child")) {
1554
+ out.push(
1555
+ `function ${P}child(el, value) {
1556
+ if (typeof value === "function") {
1557
+ el.appendChild(${P}Fragment([value]));
1558
+ } else if (value instanceof Node) {
1559
+ el.appendChild(value);
1560
+ } else if (Array.isArray(value)) {
1561
+ for (let i = 0; i < value.length; i++) {
1562
+ const item = value[i];
1563
+ if (item instanceof Node) el.appendChild(item);
1564
+ else if (item != null && typeof item !== "boolean") el.appendChild(document.createTextNode(String(item)));
1565
+ }
1566
+ } else if (value != null && typeof value !== "boolean") {
1567
+ el.appendChild(document.createTextNode(String(value)));
1568
+ }
1569
+ }`
1570
+ );
454
1571
  }
455
- return exprs;
1572
+ return out.join("\n");
456
1573
  }
457
-
458
- // src/build/staticAnalysis.ts
459
- var HTML_TAGS = /* @__PURE__ */ new Set([
460
- "div",
461
- "span",
462
- "section",
463
- "article",
464
- "header",
465
- "footer",
466
- "nav",
467
- "main",
468
- "aside",
469
- "p",
470
- "h1",
471
- "h2",
472
- "h3",
473
- "h4",
474
- "h5",
475
- "h6",
476
- "blockquote",
477
- "li",
478
- "ol",
479
- "ul",
480
- "pre",
481
- "a",
482
- "b",
483
- "em",
484
- "i",
485
- "strong",
486
- "small",
487
- "code",
488
- "mark",
489
- "img",
490
- "br",
491
- "hr",
492
- "input",
493
- "button",
494
- "form",
495
- "label",
496
- "select",
497
- "textarea",
498
- "option",
499
- "table",
500
- "tbody",
501
- "thead",
502
- "tfoot",
503
- "tr",
504
- "td",
505
- "th",
506
- "details",
507
- "summary",
508
- "dialog"
509
- ]);
510
- var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
511
- "br",
512
- "hr",
513
- "img",
514
- "input",
515
- "meta",
516
- "link",
517
- "area",
518
- "base",
519
- "col",
520
- "embed",
521
- "param",
522
- "source",
523
- "track",
524
- "wbr"
525
- ]);
526
- function analyzeStaticTemplates(code2) {
527
- const patterns = [];
528
- const tagCallRegex = new RegExp(`\\b(${Array.from(HTML_TAGS).join("|")})\\s*\\(\\s*\\{([^}]*)\\}\\s*\\)`, "g");
529
- let match2;
530
- while ((match2 = tagCallRegex.exec(code2)) !== null) {
531
- const [fullMatch, tag, propsContent] = match2;
532
- const start = match2.index;
533
- const end = start + fullMatch.length;
534
- if (isStaticPropsContent(propsContent)) {
535
- const templateHtml = propsToHtml(tag, propsContent);
536
- if (templateHtml) {
537
- patterns.push({ original: fullMatch, tag, templateHtml, start, end });
538
- }
539
- }
540
- }
541
- return {
542
- hasStaticPatterns: patterns.length > 0,
543
- patterns
544
- };
1574
+ function importSource(h) {
1575
+ const P = h.prefix;
1576
+ const core = [];
1577
+ if (h.used.has("child")) core.push(`Fragment as ${P}Fragment`);
1578
+ if (h.used.has("attr")) core.push(`registerDisposer as ${P}registerDisposer`);
1579
+ const lines = [];
1580
+ if (core.length > 0) lines.push(`import { ${core.join(", ")} } from "sibujs";`);
1581
+ if (h.used.has("attr")) lines.push(`import { bindAttrs as ${P}bindAttrs } from "sibujs/ui";`);
1582
+ return lines.join("\n");
545
1583
  }
546
- function isStaticPropsContent(propsContent) {
547
- if (/=>|function\s*\(|\bref\b|\bon\s*:/.test(propsContent)) {
548
- return false;
549
- }
550
- const props = propsContent.split(",").map((p2) => p2.trim()).filter(Boolean);
551
- for (const prop of props) {
552
- const colonIndex = prop.indexOf(":");
553
- if (colonIndex === -1) continue;
554
- const value = prop.substring(colonIndex + 1).trim();
555
- if (!isStaticValue(value)) {
556
- return false;
1584
+ function planHtmlTemplates(code2) {
1585
+ const plan = {
1586
+ edits: [],
1587
+ append: "",
1588
+ usedTags: /* @__PURE__ */ new Set(),
1589
+ usesSvg: false,
1590
+ compiledCount: 0,
1591
+ skippedCount: 0
1592
+ };
1593
+ if (!code2.includes("`") || !code2.includes("sibujs")) return plan;
1594
+ const scan = scanModule(code2);
1595
+ if (!scan) return plan;
1596
+ const tagNames = /* @__PURE__ */ new Set();
1597
+ for (const decl of scan.imports) {
1598
+ if (!isSibuSource(decl.source)) continue;
1599
+ for (const b2 of decl.bindings) if (b2.imported === "html") tagNames.add(b2.local);
1600
+ }
1601
+ if (tagNames.size === 0) return plan;
1602
+ const uses = bindingUses(code2, scan, tagNames);
1603
+ if (uses.shadowed.size > 0 || uses.calls.length > 0 || uses.tagged.length === 0) return plan;
1604
+ const sites = new Set(uses.tagged.map((ref2) => ref2.tok));
1605
+ const h = { prefix: uniquePrefix(code2), used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1606
+ const functions = [];
1607
+ const compileTemplate = (tmpl) => {
1608
+ const strings = [];
1609
+ for (const qs of tmpl.quasis) {
1610
+ const cooked = cookEscapes(code2.slice(qs.start, qs.end));
1611
+ if (cooked === null) return null;
1612
+ strings.push(cooked);
1613
+ }
1614
+ let roots;
1615
+ try {
1616
+ roots = parseTemplate(strings);
1617
+ } catch (err) {
1618
+ if (err instanceof RuntimeParseError) return null;
1619
+ throw err;
1620
+ }
1621
+ const scratch = { prefix: h.prefix, used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1622
+ const body2 = generateBody(roots, scratch);
1623
+ if (body2 === null) return null;
1624
+ for (const u2 of scratch.used) h.used.add(u2);
1625
+ for (const t of scratch.tags) h.tags.add(t);
1626
+ if (scratch.svg) h.svg = true;
1627
+ const name = `${h.prefix}t${functions.length}`;
1628
+ const params = tmpl.exprs.map((_, i2) => `v${i2}`).join(", ");
1629
+ functions.push(`function ${name}(${params}) {
1630
+ ${body2}
1631
+ }`);
1632
+ return name;
1633
+ };
1634
+ const replaceSite = (tag, tmpl, name) => {
1635
+ const edit = (start, end, before, after) => plan.edits.push({ start, end, text: `${before}${lineBreaksIn(code2, start, end)}${after}` });
1636
+ const exprs = tmpl.exprs;
1637
+ if (exprs.length === 0) {
1638
+ edit(tag.start, tmpl.end, `${name}(`, ")");
1639
+ return;
557
1640
  }
558
- }
559
- return true;
560
- }
561
- function isStaticValue(value) {
562
- if (/^["'].*["']$/.test(value)) return true;
563
- if (/^-?\d+(\.\d+)?$/.test(value)) return true;
564
- if (value === "true" || value === "false") return true;
565
- if (value === "null" || value === "undefined") return true;
566
- return false;
567
- }
568
- function propsToHtml(tag, propsContent) {
569
- const attrs = [];
570
- let nodesContent = "";
571
- const props = propsContent.split(",").map((p2) => p2.trim()).filter(Boolean);
572
- for (const prop of props) {
573
- const colonIndex = prop.indexOf(":");
574
- if (colonIndex === -1) continue;
575
- const key = prop.substring(0, colonIndex).trim();
576
- let value = prop.substring(colonIndex + 1).trim();
577
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
578
- value = value.slice(1, -1);
579
- }
580
- if (key === "nodes") {
581
- nodesContent = escapeHtml(value);
582
- } else if (key === "class" || key === "id") {
583
- attrs.push(`${key}="${escapeAttr(value)}"`);
584
- } else if (key !== "on" && key !== "ref" && key !== "style") {
585
- attrs.push(`${key}="${escapeAttr(String(value))}"`);
1641
+ edit(tag.start, exprs[0].start, `${name}((`, "");
1642
+ for (let i2 = 1; i2 < exprs.length; i2++) edit(exprs[i2 - 1].end, exprs[i2].start, ")", ", (");
1643
+ edit(exprs[exprs.length - 1].end, tmpl.end, "", "))");
1644
+ };
1645
+ const visit = (list) => {
1646
+ for (let i2 = 0; i2 < list.length; i2++) {
1647
+ const tok = list[i2];
1648
+ if (sites.has(tok)) {
1649
+ const tmpl = list[i2 + 1];
1650
+ for (const e of tmpl.exprs) visit(e.tokens);
1651
+ const name = compileTemplate(tmpl);
1652
+ if (name !== null) {
1653
+ plan.compiledCount++;
1654
+ replaceSite(tok, tmpl, name);
1655
+ } else {
1656
+ plan.skippedCount++;
1657
+ }
1658
+ i2++;
1659
+ } else if (tok.type === "template") {
1660
+ for (const e of tok.exprs) visit(e.tokens);
1661
+ }
586
1662
  }
587
- }
588
- const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
589
- if (VOID_ELEMENTS2.has(tag)) {
590
- return `<${tag}${attrStr} />`;
591
- }
592
- return `<${tag}${attrStr}>${nodesContent}</${tag}>`;
593
- }
594
- function escapeHtml(str) {
595
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
596
- }
597
- function escapeAttr(str) {
598
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1663
+ };
1664
+ visit(scan.tokens);
1665
+ if (plan.compiledCount === 0) return { ...plan, edits: [] };
1666
+ const trailer = [importSource(h), helperSource(h), ...functions].filter(Boolean).join("\n");
1667
+ plan.append = `
1668
+ ${trailer}
1669
+ `;
1670
+ plan.usedTags = h.tags;
1671
+ plan.usesSvg = h.svg;
1672
+ return plan;
599
1673
  }
600
1674
 
601
1675
  // src/build/vite.ts
602
- var SVG_TAGS_SET = /* @__PURE__ */ new Set([
603
- "svg",
604
- "circle",
605
- "ellipse",
606
- "g",
607
- "line",
608
- "path",
609
- "polygon",
610
- "polyline",
611
- "rect",
612
- "text",
613
- "tspan",
614
- "defs",
615
- "clipPath",
616
- "mask",
617
- "pattern",
618
- "linearGradient",
619
- "radialGradient",
620
- "stop",
621
- "use",
622
- "symbol",
623
- "marker"
624
- ]);
625
1676
  var DEFAULT_INCLUDE = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"];
626
1677
  var DEFAULT_EXCLUDE = ["node_modules/**", "dist/**", "**/*.test.*", "**/*.spec.*"];
627
1678
  function matchesPattern(filePath, patterns) {
@@ -631,30 +1682,42 @@ function matchesPattern(filePath, patterns) {
631
1682
  return new RegExp(regexStr).test(normalized);
632
1683
  });
633
1684
  }
634
- function injectPureAnnotations(code2) {
635
- const sibuFactories = [
636
- "tagFactory",
637
- "context",
638
- "defineComponent",
639
- "withProps",
640
- "withDefaults",
641
- "pure",
642
- "noSideEffect"
643
- ];
644
- let result = code2;
645
- for (const factory of sibuFactories) {
646
- const pattern2 = new RegExp(`(?<!/\\*#__PURE__\\*/\\s*)\\b(${factory})\\s*\\(`, "g");
647
- result = result.replace(pattern2, "/*#__PURE__*/ $1(");
1685
+ function devPrologueEdit(code2) {
1686
+ let at = 0;
1687
+ if (code2.startsWith("#!")) {
1688
+ const nl = code2.indexOf("\n");
1689
+ if (nl === -1) return null;
1690
+ at = nl + 1;
648
1691
  }
649
- return result;
1692
+ return {
1693
+ start: at,
1694
+ end: at,
1695
+ text: '/* SibuJS Dev Mode */ if (typeof globalThis !== "undefined") { globalThis.__SIBU_DEV__ = true; } '
1696
+ };
650
1697
  }
651
- function injectDevHelpers(code2) {
652
- if (code2.includes('from "sibu"') || code2.includes("from 'sibu'") || code2.includes('from "sibu/') || code2.includes("from 'sibu/")) {
653
- return `/* SibuJS Dev Mode */
654
- if (typeof globalThis !== 'undefined') { (globalThis as unknown as Record<string, unknown>).__SIBU_DEV__ = true; }
655
- ${code2}`;
656
- }
657
- return code2;
1698
+ function staticOptimizationEdits(code2) {
1699
+ const analysis = analyzeStaticTemplates(code2);
1700
+ if (!analysis.hasStaticPatterns) return null;
1701
+ const P = uniquePrefix(code2);
1702
+ const edits = analysis.patterns.map((pattern2) => ({
1703
+ start: pattern2.start,
1704
+ end: pattern2.end,
1705
+ // A call spanning several lines keeps its line breaks.
1706
+ text: `${P}static(${jsStringLiteral(pattern2.templateHtml)}${lineBreaksIn(code2, pattern2.start, pattern2.end)})`
1707
+ }));
1708
+ const append = `
1709
+ import { staticTemplate as ${P}staticTemplate } from "sibujs/performance";
1710
+ function ${P}static(markup) {
1711
+ return document.adoptNode(${P}staticTemplate(markup));
1712
+ }
1713
+ `;
1714
+ return { edits, append };
1715
+ }
1716
+ function resolveDevMode(explicit, info) {
1717
+ if (explicit !== void 0) return explicit;
1718
+ if (info?.command === "build") return info.mode === "development";
1719
+ if (info?.command === "serve") return true;
1720
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
658
1721
  }
659
1722
  function sibuVitePlugin(options = {}) {
660
1723
  const {
@@ -663,22 +1726,23 @@ function sibuVitePlugin(options = {}) {
663
1726
  include = DEFAULT_INCLUDE,
664
1727
  exclude = DEFAULT_EXCLUDE,
665
1728
  devMode,
666
- staticOptimize,
1729
+ staticOptimize = false,
667
1730
  compileTemplates
668
1731
  } = options;
669
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
1732
+ let isDevMode = resolveDevMode(devMode, void 0);
670
1733
  return {
671
1734
  name: "sibu-vite-plugin",
672
1735
  enforce: "pre",
673
- config() {
1736
+ config(_userConfig, env) {
1737
+ isDevMode = resolveDevMode(devMode, env);
674
1738
  return {
675
- // Optimize dependency pre-bundling for sibu
1739
+ // Optimize dependency pre-bundling for sibujs
676
1740
  optimizeDeps: {
677
- include: ["sibu"]
1741
+ include: ["sibujs"]
678
1742
  },
679
- // Ensure sibu is treated correctly for SSR
1743
+ // Ensure sibujs is treated correctly for SSR
680
1744
  ssr: {
681
- noExternal: ["sibu"]
1745
+ noExternal: ["sibujs"]
682
1746
  },
683
1747
  // Define global constants for dead code elimination
684
1748
  define: {
@@ -691,69 +1755,47 @@ function sibuVitePlugin(options = {}) {
691
1755
  }
692
1756
  };
693
1757
  },
1758
+ configResolved(config) {
1759
+ isDevMode = resolveDevMode(devMode, config);
1760
+ },
694
1761
  transform(code2, id) {
695
1762
  if (!matchesPattern(id, include) || matchesPattern(id, exclude)) {
696
1763
  return null;
697
1764
  }
698
- let transformed = code2;
699
- let modified = false;
700
- if (pureAnnotations) {
701
- const annotated = injectPureAnnotations(transformed);
702
- if (annotated !== transformed) {
703
- transformed = annotated;
704
- modified = true;
705
- }
1765
+ if (!code2.includes("sibujs")) return null;
1766
+ if (!scanModule(code2)) return null;
1767
+ const edits = [];
1768
+ let append = "";
1769
+ if (isDevMode && importsSibu(code2)) {
1770
+ const prologue = devPrologueEdit(code2);
1771
+ if (prologue) edits.push(prologue);
706
1772
  }
707
- if (isDevMode) {
708
- const withDevHelpers = injectDevHelpers(transformed);
709
- if (withDevHelpers !== transformed) {
710
- transformed = withDevHelpers;
711
- modified = true;
712
- }
1773
+ if (pureAnnotations) {
1774
+ edits.push(...pureAnnotationEdits(code2));
713
1775
  }
714
1776
  const shouldCompile = compileTemplates ?? !isDevMode;
715
- if (shouldCompile && transformed.includes("html`")) {
716
- const compiled = compileHtmlTemplates(transformed);
717
- if (compiled.code) {
718
- const tagImports = Array.from(compiled.usedTags).filter((t) => !SVG_TAGS_SET.has(t));
719
- const needsSvg = compiled.usesSvg;
720
- const imports = [];
721
- if (tagImports.length > 0) {
722
- imports.push(`import { ${tagImports.join(", ")} } from "sibu";`);
723
- }
724
- if (needsSvg) {
725
- imports.push(`import { tagFactory as __sbTagFactory, SVG_NS as __sbSVG_NS } from "sibu";`);
726
- }
727
- const newImports = imports.filter((imp) => !transformed.includes(imp));
728
- if (newImports.length > 0) {
729
- compiled.code = `${newImports.join("\n")}
730
- ${compiled.code}`;
731
- }
732
- transformed = compiled.code;
733
- modified = true;
1777
+ if (shouldCompile) {
1778
+ const plan = planHtmlTemplates(code2);
1779
+ if (plan.compiledCount > 0) {
1780
+ edits.push(...plan.edits);
1781
+ append += plan.append;
734
1782
  }
735
1783
  }
736
- const shouldOptimize = staticOptimize ?? !isDevMode;
737
- if (shouldOptimize) {
738
- const analysis = analyzeStaticTemplates(transformed);
739
- if (analysis.hasStaticPatterns) {
740
- const sorted = [...analysis.patterns].sort((a2, b2) => b2.start - a2.start);
741
- for (const pattern2 of sorted) {
742
- const replacement = `staticTemplate(${JSON.stringify(pattern2.templateHtml)})`;
743
- transformed = transformed.slice(0, pattern2.start) + replacement + transformed.slice(pattern2.end);
744
- }
745
- if (!transformed.includes("import") || !transformed.includes("staticTemplate")) {
746
- transformed = `import { staticTemplate } from "sibu";
747
- ${transformed}`;
748
- }
749
- modified = true;
1784
+ if (staticOptimize) {
1785
+ const optimized = staticOptimizationEdits(code2);
1786
+ if (optimized) {
1787
+ edits.push(...optimized.edits);
1788
+ append += optimized.append;
750
1789
  }
751
1790
  }
752
- if (!modified) return null;
1791
+ if (edits.length === 0 && append === "") return null;
1792
+ const sorted = normalizeEdits(edits);
1793
+ if (!sorted) return null;
1794
+ const transformed = applyEdits(code2, sorted, append);
1795
+ if (transformed === code2) return null;
753
1796
  return {
754
1797
  code: transformed,
755
- map: void 0
756
- // Let Vite handle source maps
1798
+ map: editsSourceMap(code2, sorted, append, id)
757
1799
  };
758
1800
  },
759
1801
  handleHotUpdate(ctx) {
@@ -812,7 +1854,7 @@ function createViteConfig(options = {}) {
812
1854
  // SSR configuration
813
1855
  ...ssr ? {
814
1856
  ssr: {
815
- noExternal: ["sibu"],
1857
+ noExternal: ["sibujs"],
816
1858
  target: "node"
817
1859
  },
818
1860
  build: {
@@ -829,8 +1871,8 @@ function createViteConfig(options = {}) {
829
1871
  } : {},
830
1872
  // Optimize dependency handling
831
1873
  optimizeDeps: {
832
- include: ["sibu"],
833
- // Force pre-bundling of sibu for faster dev startup
1874
+ include: ["sibujs"],
1875
+ // Force pre-bundling of sibujs for faster dev startup
834
1876
  force: false
835
1877
  },
836
1878
  // Environment variable handling
@@ -853,26 +1895,11 @@ function deepMerge(target, source2) {
853
1895
  }
854
1896
 
855
1897
  // src/build/webpack.ts
856
- var PURE_FACTORIES = [
857
- "tagFactory",
858
- "context",
859
- "defineComponent",
860
- "withProps",
861
- "withDefaults",
862
- "pure",
863
- "noSideEffect"
864
- ];
865
1898
  function addPureAnnotations(source2) {
866
- let result = source2;
867
- for (const factory of PURE_FACTORIES) {
868
- const pattern2 = new RegExp(`(?<!/\\*#__PURE__\\*/\\s*)\\b(${factory})\\s*\\(`, "g");
869
- result = result.replace(pattern2, "/*#__PURE__*/ $1(");
870
- }
871
- return result;
1899
+ return injectPureAnnotations(source2);
872
1900
  }
873
1901
  function sibuWebpackPlugin(options = {}) {
874
- const { pureAnnotations = true, devMode } = options;
875
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
1902
+ const { devMode } = options;
876
1903
  return {
877
1904
  name: "SibuWebpackPlugin",
878
1905
  apply(compiler) {
@@ -883,31 +1910,6 @@ function sibuWebpackPlugin(options = {}) {
883
1910
  });
884
1911
  }
885
1912
  });
886
- if (pureAnnotations) {
887
- compiler.hooks?.afterEnvironment?.tap("SibuWebpackPlugin", () => {
888
- if (!compiler.options.module) {
889
- compiler.options.module = { rules: [] };
890
- }
891
- if (!compiler.options.module.rules) {
892
- compiler.options.module.rules = [];
893
- }
894
- compiler.options.module.rules.push({
895
- test: /\.[jt]sx?$/,
896
- exclude: /node_modules/,
897
- enforce: "pre",
898
- use: [
899
- {
900
- loader: {
901
- // Inline loader function
902
- ident: "sibu-pure-annotations-loader",
903
- loader: "__sibu_inline_loader__",
904
- options: {}
905
- }
906
- }
907
- ]
908
- });
909
- });
910
- }
911
1913
  compiler.hooks?.afterResolvers?.tap("SibuWebpackPlugin", () => {
912
1914
  if (!compiler.options.resolve) {
913
1915
  compiler.options.resolve = {};
@@ -919,20 +1921,21 @@ function sibuWebpackPlugin(options = {}) {
919
1921
  compiler.options.resolve.mainFields.unshift("module");
920
1922
  }
921
1923
  });
922
- if (isDevMode) {
923
- compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
924
- const statsObj = stats;
925
- const info = statsObj?.toJson?.({ modules: false, chunks: false });
926
- if (info) {
927
- console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
928
- const warnings = info.warnings;
929
- if (warnings?.length) {
930
- console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
931
- }
1924
+ const tapDoneLogger = () => compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
1925
+ const statsObj = stats;
1926
+ const info = statsObj?.toJson?.({ modules: false, chunks: false });
1927
+ if (info) {
1928
+ console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
1929
+ const warnings = info.warnings;
1930
+ if (warnings?.length) {
1931
+ console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
932
1932
  }
933
- });
934
- }
1933
+ }
1934
+ });
1935
+ if (devMode === true) tapDoneLogger();
935
1936
  compiler.hooks?.environment?.tap("SibuWebpackPlugin", () => {
1937
+ const isDevMode = devMode ?? resolveWebpackDevMode(compiler.options);
1938
+ if (devMode === void 0 && isDevMode) tapDoneLogger();
936
1939
  if (!compiler.options.plugins) {
937
1940
  compiler.options.plugins = [];
938
1941
  }
@@ -940,10 +1943,20 @@ function sibuWebpackPlugin(options = {}) {
940
1943
  __SIBU_DEV__: JSON.stringify(isDevMode)
941
1944
  };
942
1945
  compiler.__sibuDefines = defines;
1946
+ const DefinePlugin = compiler.webpack?.DefinePlugin;
1947
+ if (DefinePlugin) {
1948
+ new DefinePlugin(defines).apply(compiler);
1949
+ }
943
1950
  });
944
1951
  }
945
1952
  };
946
1953
  }
1954
+ function resolveWebpackDevMode(options) {
1955
+ const mode = options.mode;
1956
+ if (mode === "development") return true;
1957
+ if (mode === "production" || mode === void 0) return false;
1958
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1959
+ }
947
1960
  function createPureAnnotationsLoader() {
948
1961
  return function sibuPureAnnotationsLoader(source2) {
949
1962
  return addPureAnnotations(source2);
@@ -1012,9 +2025,10 @@ function createWebpackConfig(options = {}) {
1012
2025
  splitChunks: isDev2 ? false : {
1013
2026
  chunks: "all",
1014
2027
  cacheGroups: {
1015
- // Separate sibu framework code into its own chunk
2028
+ // Separate sibujs framework code into its own chunk (the package
2029
+ // directory is `sibujs`; matching `sibu` never selected anything)
1016
2030
  sibu: {
1017
- test: /[\\/]node_modules[\\/]sibu[\\/]/,
2031
+ test: /[\\/]node_modules[\\/]sibujs[\\/]/,
1018
2032
  name: "sibu",
1019
2033
  chunks: "all",
1020
2034
  priority: 20
@@ -1065,7 +2079,7 @@ __export(index_exports, {
1065
2079
  Loading: () => Loading,
1066
2080
  MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
1067
2081
  Portal: () => Portal,
1068
- SVG_NS: () => SVG_NS2,
2082
+ SVG_NS: () => SVG_NS3,
1069
2083
  Suspense: () => Suspense,
1070
2084
  __resetIdCounter: () => __resetIdCounter,
1071
2085
  a: () => a,
@@ -1381,33 +2395,6 @@ function reportError(error, context2) {
1381
2395
  });
1382
2396
  }
1383
2397
 
1384
- // src/core/dev.ts
1385
- var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
1386
- // safe default: off in browser, on in test/dev Node
1387
- typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
1388
- );
1389
- function isDev() {
1390
- if (typeof globalThis.__SIBU_DEV__ !== "undefined") return !!globalThis.__SIBU_DEV__;
1391
- if (typeof __SIBU_DEV__ !== "undefined") return !!__SIBU_DEV__;
1392
- return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1393
- }
1394
- function devAssert(condition, message) {
1395
- if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
1396
- throw new Error(`[SibuJS] ${message}`);
1397
- }
1398
- }
1399
- function devWarnLazy(build) {
1400
- if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
1401
- const message = build();
1402
- if (message) console.warn(`[SibuJS] ${message}`);
1403
- }
1404
- }
1405
- function devWarn(message) {
1406
- if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
1407
- console.warn(`[SibuJS] ${message}`);
1408
- }
1409
- }
1410
-
1411
2398
  // src/core/rendering/dispose.ts
1412
2399
  var elementDisposers = /* @__PURE__ */ new WeakMap();
1413
2400
  var MAX_DRAIN_TEARDOWNS = 1e4;
@@ -1419,6 +2406,23 @@ function reportDrainRunaway(label2, executed, remaining) {
1419
2406
  );
1420
2407
  }
1421
2408
  var activeBindingCount = 0;
2409
+ function resolveCapture(capture) {
2410
+ let frame = capture;
2411
+ while (frame?.closed) frame = frame.forwardTo;
2412
+ return frame ?? null;
2413
+ }
2414
+ var registrationCaptures = [];
2415
+ function currentDisposerCapture() {
2416
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
2417
+ }
2418
+ function beginDisposerCapture(capture) {
2419
+ if (capture === null && registrationCaptures.length === 0) return false;
2420
+ registrationCaptures.push(resolveCapture(capture));
2421
+ return true;
2422
+ }
2423
+ function endDisposerCapture() {
2424
+ registrationCaptures.pop();
2425
+ }
1422
2426
  function registerDisposer(node, teardown) {
1423
2427
  let disposers = elementDisposers.get(node);
1424
2428
  if (!disposers) {
@@ -1427,297 +2431,130 @@ function registerDisposer(node, teardown) {
1427
2431
  }
1428
2432
  disposers.push(teardown);
1429
2433
  if (DEV) activeBindingCount++;
1430
- }
1431
- function unregisterDisposer(node, teardown) {
1432
- const disposers = elementDisposers.get(node);
1433
- if (!disposers) return;
1434
- const index = disposers.indexOf(teardown);
1435
- if (index === -1) return;
1436
- disposers.splice(index, 1);
1437
- if (DEV) activeBindingCount--;
1438
- if (disposers.length === 0) elementDisposers.delete(node);
1439
- }
1440
- function dispose(node) {
1441
- const stack = [node];
1442
- const order = [];
1443
- while (stack.length > 0) {
1444
- const current = stack.pop();
1445
- order.push(current);
1446
- const children = Array.from(current.childNodes);
1447
- for (let i2 = 0; i2 < children.length; i2++) {
1448
- stack.push(children[i2]);
1449
- }
1450
- }
1451
- for (let i2 = order.length - 1; i2 >= 0; i2--) {
1452
- const current = order[i2];
1453
- if (elementDisposers.has(current)) {
1454
- let executed = 0;
1455
- let runaway = false;
1456
- while (!runaway) {
1457
- const pending = elementDisposers.get(current);
1458
- if (!pending || pending.length === 0) break;
1459
- const snapshot = pending.slice();
1460
- elementDisposers.delete(current);
1461
- if (DEV) activeBindingCount -= snapshot.length;
1462
- for (let i3 = 0; i3 < snapshot.length; i3++) {
1463
- if (executed >= MAX_DRAIN_TEARDOWNS) {
1464
- const rest = snapshot.slice(i3);
1465
- const added = elementDisposers.get(current);
1466
- elementDisposers.set(current, added ? rest.concat(added) : rest);
1467
- if (DEV) activeBindingCount += rest.length;
1468
- reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1469
- runaway = true;
1470
- break;
1471
- }
1472
- executed++;
1473
- try {
1474
- snapshot[i3]();
1475
- } catch (err) {
1476
- reportError(err, { phase: "cleanup", name: "disposer" });
1477
- }
2434
+ const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
2435
+ if (capture) capture.entries.push([node, teardown]);
2436
+ }
2437
+ function withDisposerRollback(build) {
2438
+ const frame = { entries: [], closed: false };
2439
+ const captured = frame.entries;
2440
+ registrationCaptures.push(frame);
2441
+ let result;
2442
+ try {
2443
+ result = build();
2444
+ } catch (err) {
2445
+ let executed = 0;
2446
+ try {
2447
+ while (captured.length > 0) {
2448
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2449
+ reportDrainRunaway("rollback", executed, captured.length);
2450
+ break;
1478
2451
  }
1479
- }
1480
- }
1481
- }
1482
- }
1483
- function replaceChildrenSafely(parent, ...next) {
1484
- for (let i2 = 0; i2 < next.length; i2++) {
1485
- const node = next[i2];
1486
- node.parentNode?.removeChild(node);
1487
- }
1488
- const current = Array.from(parent.childNodes);
1489
- for (let i2 = 0; i2 < current.length; i2++) {
1490
- dispose(current[i2]);
1491
- }
1492
- parent.replaceChildren(...next);
1493
- }
1494
- function checkLeaks(warnThreshold = 0) {
1495
- if (!DEV) return 0;
1496
- if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
1497
- devWarn(
1498
- `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
1499
- );
1500
- }
1501
- return activeBindingCount;
1502
- }
1503
-
1504
- // src/utils/sanitize.ts
1505
- function stripControlChars(value) {
1506
- return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
1507
- }
1508
- function isEventHandlerAttr(name) {
1509
- if (name.length < 3) return false;
1510
- const lower = name.toLowerCase();
1511
- return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
1512
- }
1513
- var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1514
- var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
1515
- function sanitizeUrl(url) {
1516
- const probe = stripControlChars(url).trim();
1517
- if (!probe) return "";
1518
- const output2 = url.replace(OUTER_TRIM, "");
1519
- const lower = probe.toLowerCase();
1520
- let schemeEnd = -1;
1521
- for (let i2 = 0; i2 < lower.length; i2++) {
1522
- const ch = lower.charCodeAt(i2);
1523
- if (ch === 58) {
1524
- schemeEnd = i2;
1525
- break;
1526
- }
1527
- if (ch === 47 || ch === 63 || ch === 35) break;
1528
- }
1529
- if (schemeEnd === -1) return output2;
1530
- const scheme = lower.slice(0, schemeEnd + 1);
1531
- if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
1532
- if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1533
- return output2;
1534
- }
1535
- var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
1536
- function sanitizeSrcset(value) {
1537
- const parts = value.split(",");
1538
- const out = [];
1539
- for (let i2 = 0; i2 < parts.length; i2++) {
1540
- const part = parts[i2].trim();
1541
- if (!part) continue;
1542
- const m = part.match(/^(\S+)(\s+.+)?$/);
1543
- if (!m) continue;
1544
- if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
1545
- const safe = sanitizeUrl(m[1]);
1546
- if (!safe) continue;
1547
- out.push(m[2] ? `${safe}${m[2]}` : safe);
1548
- }
1549
- return out.join(", ");
1550
- }
1551
- var CSS_DANGER_GATE = /[(:@\\]/;
1552
- function isCssWhitespace(ch) {
1553
- return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
1554
- }
1555
- function isHexDigit(ch) {
1556
- return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
1557
- }
1558
- function decodeCssEscapes(value) {
1559
- let out = "";
1560
- for (let i2 = 0; i2 < value.length; i2++) {
1561
- const ch = value[i2];
1562
- if (ch !== "\\") {
1563
- out += ch;
1564
- continue;
1565
- }
1566
- if (i2 + 1 >= value.length) break;
1567
- const next = value[i2 + 1];
1568
- if (next === "\n" || next === "\f") {
1569
- i2 += 1;
1570
- continue;
1571
- }
1572
- if (next === "\r") {
1573
- i2 += value[i2 + 2] === "\n" ? 2 : 1;
1574
- continue;
1575
- }
1576
- if (isHexDigit(next)) {
1577
- let hex = "";
1578
- let j = i2 + 1;
1579
- while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
1580
- hex += value[j];
1581
- j++;
1582
- }
1583
- if (j < value.length && isCssWhitespace(value[j])) {
1584
- j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
1585
- }
1586
- const code2 = Number.parseInt(hex, 16);
1587
- const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
1588
- out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
1589
- i2 = j - 1;
1590
- continue;
1591
- }
1592
- out += next;
1593
- i2 += 1;
1594
- }
1595
- return out;
1596
- }
1597
- var BLOCKED_CSS_NEEDLES = [
1598
- "url(",
1599
- "expression(",
1600
- "javascript:",
1601
- "vbscript:",
1602
- "-moz-binding",
1603
- "behavior:",
1604
- "@import",
1605
- "image-set(",
1606
- "filter:progid"
1607
- ];
1608
- function describeElement(el) {
1609
- if (!el) return "an element";
1610
- let out = el.tagName.toLowerCase();
1611
- const id = el.getAttribute("id");
1612
- if (id) out += `#${id}`;
1613
- const cls = el.getAttribute("class");
1614
- if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
1615
- return `<${out}>`;
1616
- }
1617
- var warnedDeclarations = /* @__PURE__ */ new Set();
1618
- var MAX_WARNED_DECLARATIONS = 100;
1619
- var declarationCapAnnounced = false;
1620
- function warnDroppedDeclaration(value, reason, ctx) {
1621
- devWarnLazy(() => {
1622
- const reasons = [
1623
- "url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
1624
- "expression() executes arbitrary script in legacy engines",
1625
- "the javascript: scheme executes script",
1626
- "the vbscript: scheme executes script",
1627
- "-moz-binding loads and runs remote XBL",
1628
- "behavior: loads and runs a remote HTC script",
1629
- "@import pulls in a remote stylesheet",
1630
- "image-set() fetches remote images, the same exfiltration channel as url()",
1631
- "filter:progid activates legacy scriptable filters"
1632
- ];
1633
- const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
1634
- const where = describeElement(ctx?.element);
1635
- const key = `${where}|${declaration}`;
1636
- if (warnedDeclarations.has(key)) return "";
1637
- if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
1638
- if (declarationCapAnnounced) return "";
1639
- declarationCapAnnounced = true;
1640
- return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
2452
+ const [node, teardown] = captured.pop();
2453
+ if (!unregisterDisposer(node, teardown)) continue;
2454
+ executed++;
2455
+ try {
2456
+ teardown();
2457
+ } catch (cleanupErr) {
2458
+ reportError(cleanupErr, { phase: "cleanup", name: "disposer" });
2459
+ }
2460
+ }
2461
+ } finally {
2462
+ frame.closed = true;
2463
+ captured.length = 0;
2464
+ registrationCaptures.pop();
1641
2465
  }
1642
- warnedDeclarations.add(key);
1643
- return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
1644
- });
1645
- }
1646
- function blockedCssReason(value) {
1647
- if (!CSS_DANGER_GATE.test(value)) return -1;
1648
- const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
1649
- const lower = normalized.toLowerCase().replace(/\s+/g, "");
1650
- for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
1651
- if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
2466
+ throw err;
1652
2467
  }
1653
- return -1;
2468
+ frame.closed = true;
2469
+ registrationCaptures.pop();
2470
+ const parent = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
2471
+ if (parent) {
2472
+ for (const entry of captured) {
2473
+ if (elementDisposers.get(entry[0])?.includes(entry[1])) parent.entries.push(entry);
2474
+ }
2475
+ frame.forwardTo = parent;
2476
+ }
2477
+ captured.length = 0;
2478
+ return result;
1654
2479
  }
1655
- function sanitizeCSSValue(value, context2) {
1656
- const reason = blockedCssReason(value);
1657
- if (reason === -1) return value;
1658
- warnDroppedDeclaration(value, reason, context2);
1659
- return "";
2480
+ function unregisterDisposer(node, teardown) {
2481
+ const disposers = elementDisposers.get(node);
2482
+ if (!disposers) return false;
2483
+ const index = disposers.indexOf(teardown);
2484
+ if (index === -1) return false;
2485
+ disposers.splice(index, 1);
2486
+ if (DEV) activeBindingCount--;
2487
+ if (disposers.length === 0) elementDisposers.delete(node);
2488
+ return true;
1660
2489
  }
1661
- function sanitizeStyleAttribute(cssText, context2) {
1662
- const input2 = String(cssText);
1663
- if (input2.trim() === "") return "";
1664
- if (typeof document === "undefined") {
1665
- return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
2490
+ function dispose(node) {
2491
+ const stack = [node];
2492
+ const order = [];
2493
+ while (stack.length > 0) {
2494
+ const current = stack.pop();
2495
+ order.push(current);
2496
+ const children = Array.from(current.childNodes);
2497
+ for (let i2 = 0; i2 < children.length; i2++) {
2498
+ stack.push(children[i2]);
2499
+ }
1666
2500
  }
1667
- const probe = document.createElement("div");
1668
- try {
1669
- probe.style.cssText = input2;
1670
- } catch {
1671
- return "";
2501
+ for (let i2 = order.length - 1; i2 >= 0; i2--) {
2502
+ disposeNodeOwn(order[i2]);
1672
2503
  }
1673
- const declarations = [];
1674
- for (let i2 = 0; i2 < probe.style.length; i2++) {
1675
- const property = probe.style[i2];
1676
- const value = probe.style.getPropertyValue(property);
1677
- let reason = blockedCssReason(value);
1678
- if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
1679
- if (reason !== -1) {
1680
- warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1681
- continue;
2504
+ }
2505
+ function disposeNodeOwn(node) {
2506
+ if (!elementDisposers.has(node)) return;
2507
+ let executed = 0;
2508
+ let runaway = false;
2509
+ while (!runaway) {
2510
+ const pending = elementDisposers.get(node);
2511
+ if (!pending || pending.length === 0) break;
2512
+ const snapshot = pending.slice();
2513
+ elementDisposers.delete(node);
2514
+ if (DEV) activeBindingCount -= snapshot.length;
2515
+ for (let i2 = 0; i2 < snapshot.length; i2++) {
2516
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2517
+ const rest = snapshot.slice(i2);
2518
+ const added = elementDisposers.get(node);
2519
+ elementDisposers.set(node, added ? rest.concat(added) : rest);
2520
+ if (DEV) activeBindingCount += rest.length;
2521
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
2522
+ runaway = true;
2523
+ break;
2524
+ }
2525
+ executed++;
2526
+ try {
2527
+ snapshot[i2]();
2528
+ } catch (err) {
2529
+ reportError(err, { phase: "cleanup", name: "disposer" });
2530
+ }
1682
2531
  }
1683
- const priority = probe.style.getPropertyPriority(property);
1684
- declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
1685
2532
  }
1686
- return declarations.join("; ");
1687
2533
  }
1688
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1689
- "href",
1690
- "xlink:href",
1691
- "src",
1692
- "action",
1693
- "formaction",
1694
- "formtarget",
1695
- "cite",
1696
- "poster",
1697
- "background",
1698
- "srcset",
1699
- "ping",
1700
- "data",
1701
- // `<html manifest>` is a URL sink too. It was carried only by the SSR
1702
- // serializers' private copy of this set; folding that copy into this one would
1703
- // otherwise have silently dropped its coverage.
1704
- "manifest"
1705
- ]);
1706
- var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
1707
- function isHtmlContentAttribute(name) {
1708
- return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
2534
+ function replaceChildrenSafely(parent, ...next) {
2535
+ for (let i2 = 0; i2 < next.length; i2++) {
2536
+ const node = next[i2];
2537
+ node.parentNode?.removeChild(node);
2538
+ }
2539
+ const current = Array.from(parent.childNodes);
2540
+ for (let i2 = 0; i2 < current.length; i2++) {
2541
+ dispose(current[i2]);
2542
+ }
2543
+ parent.replaceChildren(...next);
1709
2544
  }
1710
- function sanitizeAttributeString(attr, value, context2) {
1711
- const lower = attr.toLowerCase();
1712
- if (lower === "srcset") return sanitizeSrcset(value);
1713
- if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1714
- if (lower === "style") return sanitizeStyleAttribute(value, context2);
1715
- return value;
2545
+ function checkLeaks(warnThreshold = 0) {
2546
+ if (!DEV) return 0;
2547
+ if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
2548
+ devWarn(
2549
+ `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
2550
+ );
2551
+ }
2552
+ return activeBindingCount;
1716
2553
  }
1717
2554
 
1718
2555
  // src/utils/setSafeAttribute.ts
1719
2556
  var HTML_NS = "http://www.w3.org/1999/xhtml";
1720
- var SVG_NS = "http://www.w3.org/2000/svg";
2557
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
1721
2558
  var XLINK_NS = "http://www.w3.org/1999/xlink";
1722
2559
  var XML_NS = "http://www.w3.org/XML/1998/namespace";
1723
2560
  var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
@@ -1728,7 +2565,7 @@ function setProp(el, key, val) {
1728
2565
  el[key] = val;
1729
2566
  }
1730
2567
  function namespaceFor(el, name) {
1731
- if (el.namespaceURI !== SVG_NS) return null;
2568
+ if (el.namespaceURI !== SVG_NS2) return null;
1732
2569
  const lower = name.toLowerCase();
1733
2570
  if (lower.startsWith("xlink:")) return XLINK_NS;
1734
2571
  if (lower.startsWith("xml:")) return XML_NS;
@@ -1956,6 +2793,7 @@ function retrack(effectFn, subscriber) {
1956
2793
  }
1957
2794
  currentSubscriber = subscriber;
1958
2795
  const sub2 = subscriber;
2796
+ if (sub2._cap === void 0) sub2._cap = currentDisposerCapture();
1959
2797
  const epoch = ++subscriberEpochCounter;
1960
2798
  sub2._epoch = epoch;
1961
2799
  sub2._structDirty = false;
@@ -2147,7 +2985,12 @@ function drainQueue() {
2147
2985
  quarantined.add(sub2);
2148
2986
  continue;
2149
2987
  }
2150
- safeInvoke(sub2);
2988
+ const pushed = beginDisposerCapture(sub2._cap ?? null);
2989
+ try {
2990
+ safeInvoke(sub2);
2991
+ } finally {
2992
+ if (pushed) endDisposerCapture();
2993
+ }
2151
2994
  }
2152
2995
  }
2153
2996
  function drainNotificationQueue() {
@@ -2283,7 +3126,7 @@ function forEachSubscriber(signal2, visit) {
2283
3126
  }
2284
3127
 
2285
3128
  // src/reactivity/track.ts
2286
- var _runtimeVersion = true ? "4.5.0" : "dev";
3129
+ var _runtimeVersion = true ? "4.7.0" : "dev";
2287
3130
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
2288
3131
  function resolveReactiveApi() {
2289
3132
  const g2 = globalThis;
@@ -2560,7 +3403,7 @@ function bindChildNode(placeholder, getter) {
2560
3403
  }
2561
3404
 
2562
3405
  // src/core/rendering/tagFactory.ts
2563
- var SVG_NS2 = "http://www.w3.org/2000/svg";
3406
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
2564
3407
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
2565
3408
  function isBlockedTag(tag) {
2566
3409
  return BLOCKED_TAGS.has(tag.toLowerCase());
@@ -2591,8 +3434,34 @@ function toKebab(prop) {
2591
3434
  }
2592
3435
  function applyStyle(el, style2) {
2593
3436
  if (typeof style2 === "function") {
3437
+ const getter = style2;
3438
+ let written = [];
3439
+ let wroteString = false;
2594
3440
  const teardown = reactiveBinding2(() => {
2595
- el.setAttribute("style", sanitizeStyleAttribute(String(style2()), { element: el }));
3441
+ const value = getter();
3442
+ if (value === null || typeof value !== "object") {
3443
+ written = [];
3444
+ wroteString = true;
3445
+ el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
3446
+ return;
3447
+ }
3448
+ const decl = el.style;
3449
+ if (wroteString) {
3450
+ el.removeAttribute("style");
3451
+ wroteString = false;
3452
+ }
3453
+ const next = [];
3454
+ for (const prop in value) {
3455
+ const val = value[prop];
3456
+ if (val == null) continue;
3457
+ const name = toKebab(prop);
3458
+ next.push(name);
3459
+ decl.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
3460
+ }
3461
+ for (let i2 = 0; i2 < written.length; i2++) {
3462
+ if (next.indexOf(written[i2]) === -1) decl.removeProperty(written[i2]);
3463
+ }
3464
+ written = next;
2596
3465
  }, el);
2597
3466
  registerDisposer(el, teardown);
2598
3467
  return;
@@ -2623,7 +3492,9 @@ function applyClass(el, cls) {
2623
3492
  }
2624
3493
  if (typeof cls === "function") {
2625
3494
  const teardown = reactiveBinding2(() => {
2626
- el.setAttribute("class", cls());
3495
+ const value = cls();
3496
+ if (value == null || value === false) el.removeAttribute("class");
3497
+ else el.setAttribute("class", value);
2627
3498
  }, el);
2628
3499
  registerDisposer(el, teardown);
2629
3500
  return;
@@ -2705,7 +3576,7 @@ function appendChildren(el, nodes) {
2705
3576
  }
2706
3577
  }
2707
3578
  }
2708
- var tagFactory = (tag, ns) => {
3579
+ function tagFactory(tag, ns) {
2709
3580
  const blocked = isBlockedTag(tag);
2710
3581
  return (first, second) => {
2711
3582
  if (blocked) {
@@ -2754,7 +3625,9 @@ var tagFactory = (tag, ns) => {
2754
3625
  const pClass = props.class;
2755
3626
  if (pClass != null) applyClass(el, pClass);
2756
3627
  const pId = props.id;
2757
- if (pId != null) {
3628
+ if (typeof pId === "function") {
3629
+ registerDisposer(el, bindAttribute(el, "id", pId));
3630
+ } else if (pId != null) {
2758
3631
  if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2759
3632
  devWarn(
2760
3633
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
@@ -2804,12 +3677,10 @@ var tagFactory = (tag, ns) => {
2804
3677
  }
2805
3678
  }
2806
3679
  }
2807
- if (props.onElement && typeof props.onElement === "function") {
2808
- props.onElement(el);
2809
- }
3680
+ if (typeof props.onElement === "function") props.onElement(el);
2810
3681
  return el;
2811
3682
  };
2812
- };
3683
+ }
2813
3684
 
2814
3685
  // src/core/rendering/html.ts
2815
3686
  var html = tagFactory("html");
@@ -2884,7 +3755,7 @@ var param = tagFactory("param");
2884
3755
  var picture = tagFactory("picture");
2885
3756
  var portal = tagFactory("portal");
2886
3757
  var source = tagFactory("source");
2887
- var svg = tagFactory("svg", SVG_NS2);
3758
+ var svg = tagFactory("svg", SVG_NS3);
2888
3759
  var math = tagFactory("math");
2889
3760
  var canvas = tagFactory("canvas");
2890
3761
  var noscript = tagFactory("noscript");
@@ -2925,26 +3796,26 @@ var base = tagFactory("base");
2925
3796
  var link = tagFactory("link");
2926
3797
  var meta = tagFactory("meta");
2927
3798
  var style = tagFactory("style");
2928
- var circle = tagFactory("circle", SVG_NS2);
2929
- var ellipse = tagFactory("ellipse", SVG_NS2);
2930
- var g = tagFactory("g", SVG_NS2);
2931
- var line = tagFactory("line", SVG_NS2);
2932
- var path = tagFactory("path", SVG_NS2);
2933
- var polygon = tagFactory("polygon", SVG_NS2);
2934
- var polyline = tagFactory("polyline", SVG_NS2);
2935
- var rect = tagFactory("rect", SVG_NS2);
2936
- var text = tagFactory("text", SVG_NS2);
2937
- var tspan = tagFactory("tspan", SVG_NS2);
2938
- var defs = tagFactory("defs", SVG_NS2);
2939
- var clipPath = tagFactory("clipPath", SVG_NS2);
2940
- var mask = tagFactory("mask", SVG_NS2);
2941
- var pattern = tagFactory("pattern", SVG_NS2);
2942
- var linearGradient = tagFactory("linearGradient", SVG_NS2);
2943
- var radialGradient = tagFactory("radialGradient", SVG_NS2);
2944
- var stop = tagFactory("stop", SVG_NS2);
2945
- var use = tagFactory("use", SVG_NS2);
2946
- var symbol = tagFactory("symbol", SVG_NS2);
2947
- var marker = tagFactory("marker", SVG_NS2);
3799
+ var circle = tagFactory("circle", SVG_NS3);
3800
+ var ellipse = tagFactory("ellipse", SVG_NS3);
3801
+ var g = tagFactory("g", SVG_NS3);
3802
+ var line = tagFactory("line", SVG_NS3);
3803
+ var path = tagFactory("path", SVG_NS3);
3804
+ var polygon = tagFactory("polygon", SVG_NS3);
3805
+ var polyline = tagFactory("polyline", SVG_NS3);
3806
+ var rect = tagFactory("rect", SVG_NS3);
3807
+ var text = tagFactory("text", SVG_NS3);
3808
+ var tspan = tagFactory("tspan", SVG_NS3);
3809
+ var defs = tagFactory("defs", SVG_NS3);
3810
+ var clipPath = tagFactory("clipPath", SVG_NS3);
3811
+ var mask = tagFactory("mask", SVG_NS3);
3812
+ var pattern = tagFactory("pattern", SVG_NS3);
3813
+ var linearGradient = tagFactory("linearGradient", SVG_NS3);
3814
+ var radialGradient = tagFactory("radialGradient", SVG_NS3);
3815
+ var stop = tagFactory("stop", SVG_NS3);
3816
+ var use = tagFactory("use", SVG_NS3);
3817
+ var symbol = tagFactory("symbol", SVG_NS3);
3818
+ var marker = tagFactory("marker", SVG_NS3);
2948
3819
  var center = tagFactory("center");
2949
3820
  var font = tagFactory("font");
2950
3821
  var marquee = tagFactory("marquee");
@@ -3058,10 +3929,13 @@ function safeCall(cb, hookName) {
3058
3929
  return void 0;
3059
3930
  }
3060
3931
  }
3061
- function runMountCallback(callback, hookName, element) {
3932
+ function runMountCallback(callback, hookName, element, isDisposed) {
3062
3933
  const cleanup3 = safeCall(callback, hookName);
3063
- if (typeof cleanup3 === "function" && element) {
3064
- registerDisposer(element, cleanup3);
3934
+ if (typeof cleanup3 !== "function") return;
3935
+ if (isDisposed() || !element.isConnected) {
3936
+ safeCall(cleanup3, "onUnmount");
3937
+ } else {
3938
+ onUnmount(cleanup3, element);
3065
3939
  }
3066
3940
  }
3067
3941
  var mountWatchers = /* @__PURE__ */ new WeakMap();
@@ -3223,25 +4097,26 @@ function onMount(callback, element) {
3223
4097
  if (typeof document === "undefined") return;
3224
4098
  if (element) {
3225
4099
  let disposed = false;
4100
+ const isDisposed = () => disposed;
3226
4101
  registerDisposer(element, () => {
3227
4102
  disposed = true;
3228
4103
  });
3229
4104
  if (element.isConnected) {
3230
4105
  queueMicrotask(() => {
3231
4106
  if (disposed) return;
3232
- runMountCallback(callback, "onMount", element);
4107
+ runMountCallback(callback, "onMount", element, isDisposed);
3233
4108
  });
3234
4109
  return;
3235
4110
  }
3236
4111
  queueMicrotask(() => {
3237
4112
  if (disposed) return;
3238
4113
  if (element.isConnected) {
3239
- runMountCallback(callback, "onMount", element);
4114
+ runMountCallback(callback, "onMount", element, isDisposed);
3240
4115
  return;
3241
4116
  }
3242
4117
  const unregister = registerMountWatcher(element, () => {
3243
4118
  if (disposed) return;
3244
- runMountCallback(callback, "onMount", element);
4119
+ runMountCallback(callback, "onMount", element, isDisposed);
3245
4120
  });
3246
4121
  registerDisposer(element, unregister);
3247
4122
  });
@@ -3278,6 +4153,14 @@ function onCleanup(callback, element) {
3278
4153
  registerDisposer(element, callback);
3279
4154
  }
3280
4155
 
4156
+ // src/core/devtoolsHook.ts
4157
+ function emitDevtools(hook, event, payload) {
4158
+ try {
4159
+ hook.emit(event, payload);
4160
+ } catch {
4161
+ }
4162
+ }
4163
+
3281
4164
  // src/core/ssr-context.ts
3282
4165
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
3283
4166
  function detectSSRShared() {
@@ -3324,6 +4207,10 @@ function getSSRStore() {
3324
4207
  }
3325
4208
  return fallbackStore;
3326
4209
  }
4210
+ function getRequestStore() {
4211
+ if (als) return als.getStore() ?? null;
4212
+ return _shared.fallbackDepth > 0 ? fallbackStore : null;
4213
+ }
3327
4214
  function isSSR() {
3328
4215
  return getSSRStore().ssr;
3329
4216
  }
@@ -3345,16 +4232,18 @@ function disableSSR() {
3345
4232
  getSSRStore().ssr = false;
3346
4233
  }
3347
4234
  function runInSSRContext(fn) {
3348
- const store2 = { ssr: true, suspenseIdCounter: 0, locale: void 0 };
4235
+ const store2 = { ssr: true, suspenseIdCounter: 0, idCounter: 0, locale: void 0 };
3349
4236
  if (als) {
3350
4237
  return als.run(store2, fn);
3351
4238
  }
3352
4239
  warnMissingAsyncLocalStorage();
3353
4240
  const prevSSR = fallbackStore.ssr;
3354
4241
  const prevCounter = fallbackStore.suspenseIdCounter;
4242
+ const prevIdCounter = fallbackStore.idCounter;
3355
4243
  const prevLocale = fallbackStore.locale;
3356
4244
  fallbackStore.ssr = true;
3357
4245
  fallbackStore.suspenseIdCounter = 0;
4246
+ fallbackStore.idCounter = 0;
3358
4247
  fallbackStore.locale = void 0;
3359
4248
  _shared.fallbackDepth++;
3360
4249
  try {
@@ -3363,6 +4252,7 @@ function runInSSRContext(fn) {
3363
4252
  _shared.fallbackDepth--;
3364
4253
  fallbackStore.ssr = prevSSR;
3365
4254
  fallbackStore.suspenseIdCounter = prevCounter;
4255
+ fallbackStore.idCounter = prevIdCounter;
3366
4256
  fallbackStore.locale = prevLocale;
3367
4257
  }
3368
4258
  }
@@ -3508,12 +4398,17 @@ function effect(effectFn, options) {
3508
4398
  try {
3509
4399
  retrack2(ctx.bodyFn, ctx.subscriber);
3510
4400
  if (ctx.rerunPending) drainReruns(ctx);
4401
+ } catch (err) {
4402
+ ctx.disposed = true;
4403
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
4404
+ cleanup2(ctx.subscriber);
4405
+ throw err;
3511
4406
  } finally {
3512
4407
  ctx.running = false;
3513
4408
  ctx.rerunPending = false;
3514
4409
  }
3515
4410
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3516
- if (hook) hook.emit("effect:create", { effectFn });
4411
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
3517
4412
  return () => disposeEffect(ctx);
3518
4413
  }
3519
4414
 
@@ -3600,7 +4495,7 @@ function signal(initial, options) {
3600
4495
  state2.__v++;
3601
4496
  if (DEV) {
3602
4497
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3603
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4498
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3604
4499
  }
3605
4500
  if (!enqueueBatchedSignal(state2)) {
3606
4501
  notifySubscribers2(state2);
@@ -3614,7 +4509,7 @@ function signal(initial, options) {
3614
4509
  state2.value = newValue;
3615
4510
  state2.__v++;
3616
4511
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3617
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4512
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3618
4513
  if (!enqueueBatchedSignal(state2)) {
3619
4514
  notifySubscribers2(state2);
3620
4515
  }
@@ -3633,7 +4528,7 @@ function signal(initial, options) {
3633
4528
  }
3634
4529
  if (DEV) {
3635
4530
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3636
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
4531
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
3637
4532
  }
3638
4533
  return [get, set];
3639
4534
  }
@@ -4446,36 +5341,34 @@ function Loading(props = {}) {
4446
5341
  const { text: text2, variant = "spinner", size = "md" } = props;
4447
5342
  const sizeClass = size !== "md" ? ` sibu-loading-${size}` : "";
4448
5343
  if (variant === "dots") {
4449
- return div({
5344
+ return div(
5345
+ {
5346
+ class: `sibu-loading${sizeClass}`,
5347
+ role: "status",
5348
+ "aria-live": "polite",
5349
+ // When there's no visible text, give the live region an accessible name
5350
+ // so it isn't announced as an empty status.
5351
+ "aria-label": text2 ? void 0 : "Loading"
5352
+ },
5353
+ [
5354
+ div({ class: "sibu-loading-dots" }, [
5355
+ span({ class: "sibu-loading-dot" }),
5356
+ span({ class: "sibu-loading-dot" }),
5357
+ span({ class: "sibu-loading-dot" })
5358
+ ]),
5359
+ text2 ? span({ class: "sibu-loading-text" }, text2) : null
5360
+ ]
5361
+ );
5362
+ }
5363
+ return div(
5364
+ {
4450
5365
  class: `sibu-loading${sizeClass}`,
4451
5366
  role: "status",
4452
5367
  "aria-live": "polite",
4453
- // When there's no visible text, give the live region an accessible name
4454
- // so it isn't announced as an empty status.
4455
- "aria-label": text2 ? void 0 : "Loading",
4456
- nodes: [
4457
- div({
4458
- class: "sibu-loading-dots",
4459
- nodes: [
4460
- span({ class: "sibu-loading-dot" }),
4461
- span({ class: "sibu-loading-dot" }),
4462
- span({ class: "sibu-loading-dot" })
4463
- ]
4464
- }),
4465
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4466
- ].filter(Boolean)
4467
- });
4468
- }
4469
- return div({
4470
- class: `sibu-loading${sizeClass}`,
4471
- role: "status",
4472
- "aria-live": "polite",
4473
- "aria-label": text2 ? void 0 : "Loading",
4474
- nodes: [
4475
- div({ class: "sibu-loading-spinner" }),
4476
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4477
- ].filter(Boolean)
4478
- });
5368
+ "aria-label": text2 ? void 0 : "Loading"
5369
+ },
5370
+ [div({ class: "sibu-loading-spinner" }), text2 ? span({ class: "sibu-loading-text" }, text2) : null]
5371
+ );
4479
5372
  }
4480
5373
 
4481
5374
  // src/core/rendering/action.ts
@@ -4534,9 +5427,14 @@ var longPress = (element, options) => {
4534
5427
  };
4535
5428
  };
4536
5429
  var copyOnClick = (element, getText) => {
5430
+ const report = (error) => reportError(error, { phase: "async", name: "copyOnClick", node: element });
4537
5431
  const handler = () => {
4538
- const text2 = typeof getText === "function" ? getText() : element.textContent ?? "";
4539
- navigator.clipboard.writeText(text2);
5432
+ try {
5433
+ const text2 = typeof getText === "function" ? getText() : element.textContent ?? "";
5434
+ Promise.resolve(navigator.clipboard.writeText(text2)).catch(report);
5435
+ } catch (error) {
5436
+ report(error);
5437
+ }
4540
5438
  };
4541
5439
  element.addEventListener("click", handler);
4542
5440
  return () => element.removeEventListener("click", handler);
@@ -4576,13 +5474,35 @@ registerAction("copyOnClick", copyOnClick);
4576
5474
  registerAction("autoResize", autoResize);
4577
5475
  registerAction("trapFocus", trapFocus);
4578
5476
 
5477
+ // src/utils/adoptThenable.ts
5478
+ function adoptThenable(value) {
5479
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
5480
+ let then;
5481
+ try {
5482
+ then = value.then;
5483
+ } catch (error) {
5484
+ return Promise.reject(error);
5485
+ }
5486
+ if (typeof then !== "function") return null;
5487
+ return new Promise((resolve, reject) => {
5488
+ queueMicrotask(() => {
5489
+ try {
5490
+ Reflect.apply(then, value, [resolve, reject]);
5491
+ } catch (error) {
5492
+ reject(error);
5493
+ }
5494
+ });
5495
+ });
5496
+ }
5497
+
4579
5498
  // src/core/rendering/catch.ts
4580
5499
  var globalErrorHandler = null;
4581
5500
  function catchError(fn, onError) {
4582
5501
  try {
4583
5502
  const result = fn();
4584
- if (result && typeof result.then === "function") {
4585
- result.catch((err) => {
5503
+ const adopted = adoptThenable(result);
5504
+ if (adopted) {
5505
+ adopted.then(void 0, (err) => {
4586
5506
  if (onError) {
4587
5507
  onError(err, "async");
4588
5508
  } else if (globalErrorHandler) {
@@ -4672,6 +5592,11 @@ function context(defaultValue) {
4672
5592
  var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
4673
5593
  var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
4674
5594
  function createId(prefix = "sibu") {
5595
+ const request = getRequestStore();
5596
+ if (request) {
5597
+ request.idCounter = (request.idCounter ?? 0) + 1;
5598
+ return `${prefix}-${request.idCounter}`;
5599
+ }
4675
5600
  _counter.n++;
4676
5601
  return `${prefix}-${_counter.n}`;
4677
5602
  }
@@ -4709,13 +5634,15 @@ function when(condition, thenBranch, elseBranch) {
4709
5634
  let currentNode = null;
4710
5635
  let lastCondition;
4711
5636
  let initialized = false;
5637
+ let disposed = false;
4712
5638
  const attachedOnce = /* @__PURE__ */ new WeakSet();
4713
5639
  let currentNodeOwned = false;
4714
5640
  const update = () => {
5641
+ if (disposed) return;
4715
5642
  const show2 = condition();
4716
5643
  const parent = anchor.parentNode;
4717
5644
  if (!parent) return;
4718
- if (initialized && show2 === lastCondition) return;
5645
+ if (initialized && Object.is(show2, lastCondition)) return;
4719
5646
  lastCondition = show2;
4720
5647
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
4721
5648
  if (currentNode?.parentNode) {
@@ -4724,16 +5651,20 @@ function when(condition, thenBranch, elseBranch) {
4724
5651
  currentNode = null;
4725
5652
  }
4726
5653
  const branch = show2 ? thenBranch : elseBranch !== void 0 ? elseBranch : null;
4727
- const attached = attachBranch(parent, anchor, branch, attachedOnce, "when");
5654
+ const attached = untracked2(() => attachBranch(parent, anchor, branch, attachedOnce, "when"));
4728
5655
  currentNode = attached.node;
4729
5656
  currentNodeOwned = attached.owned;
4730
5657
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "when");
4731
5658
  initialized = true;
4732
5659
  };
4733
- registerDisposer(anchor, reactiveBinding2(update, anchor));
5660
+ const stopBinding = reactiveBinding2(update, anchor);
5661
+ registerDisposer(anchor, () => {
5662
+ disposed = true;
5663
+ stopBinding();
5664
+ });
4734
5665
  if (!initialized) {
4735
5666
  queueMicrotask(() => {
4736
- if (!initialized && anchor.parentNode) update();
5667
+ if (!disposed && !initialized && anchor.parentNode) update();
4737
5668
  });
4738
5669
  }
4739
5670
  return anchor;
@@ -4743,9 +5674,11 @@ function match(value, cases, fallback) {
4743
5674
  let currentNode = null;
4744
5675
  let lastKey;
4745
5676
  let initialized = false;
5677
+ let disposed = false;
4746
5678
  const attachedOnce = /* @__PURE__ */ new WeakSet();
4747
5679
  let currentNodeOwned = false;
4748
5680
  const update = () => {
5681
+ if (disposed) return;
4749
5682
  const key = String(value());
4750
5683
  const parent = anchor.parentNode;
4751
5684
  if (!parent) return;
@@ -4758,46 +5691,72 @@ function match(value, cases, fallback) {
4758
5691
  currentNode = null;
4759
5692
  }
4760
5693
  const branch = Object.hasOwn(cases, key) ? cases[key] : fallback;
4761
- const attached = attachBranch(parent, anchor, branch ?? null, attachedOnce, "match");
5694
+ const attached = untracked2(() => attachBranch(parent, anchor, branch ?? null, attachedOnce, "match"));
4762
5695
  currentNode = attached.node;
4763
5696
  currentNodeOwned = attached.owned;
4764
5697
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "match");
4765
5698
  initialized = true;
4766
5699
  };
4767
- registerDisposer(anchor, reactiveBinding2(update, anchor));
5700
+ const stopBinding = reactiveBinding2(update, anchor);
5701
+ registerDisposer(anchor, () => {
5702
+ disposed = true;
5703
+ stopBinding();
5704
+ });
4768
5705
  if (!initialized) {
4769
5706
  queueMicrotask(() => {
4770
- if (!initialized && anchor.parentNode) update();
5707
+ if (!disposed && !initialized && anchor.parentNode) update();
4771
5708
  });
4772
5709
  }
4773
5710
  return anchor;
4774
5711
  }
4775
5712
 
5713
+ // src/utils/globalSingleton.ts
5714
+ function globalSingleton(key, create) {
5715
+ const g2 = globalThis;
5716
+ return g2[key] ?? (g2[key] = create());
5717
+ }
5718
+
4776
5719
  // src/core/rendering/dynamic.ts
4777
- var componentRegistry = /* @__PURE__ */ new Map();
5720
+ var componentRegistry = globalSingleton(
5721
+ /* @__PURE__ */ Symbol.for("sibujs.components.registry.v1"),
5722
+ () => /* @__PURE__ */ new Map()
5723
+ );
5724
+ var registryVersion = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.components.registryVersion.v1"), () => signal(0));
5725
+ function bumpRegistry() {
5726
+ const [read, write] = registryVersion;
5727
+ write(untracked2(read) + 1);
5728
+ }
4778
5729
  function registerComponent(name, component) {
4779
5730
  componentRegistry.set(name, component);
5731
+ bumpRegistry();
4780
5732
  }
4781
5733
  function unregisterComponent(name) {
4782
- componentRegistry.delete(name);
5734
+ if (componentRegistry.delete(name)) bumpRegistry();
4783
5735
  }
4784
5736
  function resolveComponent(name) {
4785
5737
  const component = componentRegistry.get(name);
4786
5738
  if (component) {
4787
5739
  return component();
4788
5740
  }
4789
- return div({ nodes: `[Component "${name}" not found]` });
5741
+ return div(`[Component "${name}" not found]`);
4790
5742
  }
4791
5743
  function DynamicComponent(is) {
4792
5744
  const container = div({ class: "sibu-dynamic" });
5745
+ const NONE = {};
5746
+ let shown = NONE;
4793
5747
  function render() {
4794
5748
  const target = is();
4795
- let el;
5749
+ let component;
4796
5750
  if (typeof target === "function") {
4797
- el = target();
5751
+ component = target;
4798
5752
  } else {
4799
- el = resolveComponent(target);
5753
+ registryVersion[0]();
5754
+ component = componentRegistry.get(target);
4800
5755
  }
5756
+ const next = component ?? `missing:${target}`;
5757
+ if (next === shown) return;
5758
+ const el = untracked2(() => component ? component() : div(`[Component "${target}" not found]`));
5759
+ shown = next;
4801
5760
  replaceChildrenSafely(container, el);
4802
5761
  }
4803
5762
  const untrack = track2(render);
@@ -5061,31 +6020,30 @@ function Fragment(nodes) {
5061
6020
  if (Array.isArray(child)) {
5062
6021
  for (const nested of child) {
5063
6022
  if (nested == null || typeof nested === "boolean") continue;
5064
- frag.appendChild(resolveChild(nested));
6023
+ appendChild(frag, nested);
5065
6024
  }
5066
6025
  } else {
5067
- frag.appendChild(resolveChild(child));
6026
+ appendChild(frag, child);
5068
6027
  }
5069
6028
  }
5070
6029
  return frag;
5071
6030
  }
5072
- function resolveChild(child) {
5073
- if (child == null) {
5074
- return document.createTextNode("");
5075
- }
6031
+ function appendChild(frag, child) {
5076
6032
  if (child instanceof Node) {
5077
- return child;
6033
+ frag.appendChild(child);
6034
+ return;
5078
6035
  }
5079
6036
  if (typeof child === "function") {
5080
- const result = child();
5081
- if (result instanceof Node) return result;
5082
- return document.createTextNode(String(result ?? ""));
6037
+ const placeholder = document.createComment("");
6038
+ frag.appendChild(placeholder);
6039
+ registerDisposer(placeholder, bindChildNode(placeholder, child));
6040
+ return;
5083
6041
  }
5084
- return document.createTextNode(String(child));
6042
+ frag.appendChild(document.createTextNode(String(child)));
5085
6043
  }
5086
6044
 
5087
6045
  // src/core/rendering/htm.ts
5088
- var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
6046
+ var RAW_TEXT_TAGS2 = /* @__PURE__ */ new Set(["script", "style"]);
5089
6047
  var VOID_ELEMENTS3 = /* @__PURE__ */ new Set([
5090
6048
  "area",
5091
6049
  "base",
@@ -5126,7 +6084,7 @@ var SVG_TAGS2 = /* @__PURE__ */ new Set([
5126
6084
  "marker"
5127
6085
  ]);
5128
6086
  var cache = /* @__PURE__ */ new WeakMap();
5129
- function parseTemplate(strings) {
6087
+ function parseTemplate2(strings) {
5130
6088
  const exprCount = strings.length - 1;
5131
6089
  let template2 = strings[0];
5132
6090
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -5308,7 +6266,7 @@ function parseTemplate(strings) {
5308
6266
  children.push({ t: 0, el: { tag, svg: SVG_TAGS2.has(tag), attrs, children: [] } });
5309
6267
  } else {
5310
6268
  const inner = parseChildren();
5311
- if (RAW_TEXT_TAGS.has(tag.toLowerCase())) {
6269
+ if (RAW_TEXT_TAGS2.has(tag.toLowerCase())) {
5312
6270
  for (let i2 = 0; i2 < inner.length; i2++) {
5313
6271
  if (inner[i2].t === 2) {
5314
6272
  throw new Error(
@@ -5334,7 +6292,7 @@ function parseTemplate(strings) {
5334
6292
  return parseChildren();
5335
6293
  }
5336
6294
  function executeElement(tmpl, values) {
5337
- const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
6295
+ const el = tmpl.svg ? document.createElementNS(SVG_NS3, tmpl.tag) : document.createElement(tmpl.tag);
5338
6296
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
5339
6297
  const attr = tmpl.attrs[i2];
5340
6298
  switch (attr.t) {
@@ -5414,7 +6372,7 @@ function executeElement(tmpl, values) {
5414
6372
  function html2(strings, ...values) {
5415
6373
  let tmpl = cache.get(strings);
5416
6374
  if (!tmpl) {
5417
- tmpl = parseTemplate(strings);
6375
+ tmpl = parseTemplate2(strings);
5418
6376
  cache.set(strings, tmpl);
5419
6377
  }
5420
6378
  if (tmpl.length === 1 && tmpl[0].t === 0) {
@@ -5551,25 +6509,77 @@ function mount(component, container) {
5551
6509
  typeof component === "function" || component instanceof Node,
5552
6510
  "mount: first argument must be a component function or a DOM Node."
5553
6511
  );
5554
- const startTime = typeof performance !== "undefined" ? performance.now() : 0;
5555
- const node = typeof component === "function" ? component() : component;
5556
- const duration = typeof performance !== "undefined" ? performance.now() - startTime : 0;
5557
- container.appendChild(node);
5558
- const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5559
- if (hook) {
5560
- hook.emit("app:init", { rootElement: node, container, duration });
6512
+ const now = () => typeof performance !== "undefined" ? performance.now() : 0;
6513
+ const startTime = now();
6514
+ let range = null;
6515
+ const attach = (built) => {
6516
+ if (built instanceof DocumentFragment) {
6517
+ const start = document.createComment("");
6518
+ const end = document.createComment("");
6519
+ built.insertBefore(start, built.firstChild);
6520
+ built.appendChild(end);
6521
+ range = { start, end };
6522
+ }
6523
+ container.appendChild(built);
6524
+ };
6525
+ let node;
6526
+ if (typeof component === "function") {
6527
+ node = withDisposerRollback(() => {
6528
+ const built = component();
6529
+ attach(built);
6530
+ return built;
6531
+ });
6532
+ } else {
6533
+ node = component;
6534
+ attach(node);
5561
6535
  }
6536
+ const duration = now() - startTime;
6537
+ const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6538
+ if (hook) emitDevtools(hook, "app:init", { rootElement: node, container, duration });
6539
+ const mounted = range;
5562
6540
  return {
5563
6541
  node,
5564
6542
  unmount() {
5565
- if (hook) hook.emit("app:unmount", { rootElement: node });
5566
- dispose(node);
5567
- if (node.parentNode) {
5568
- node.parentNode.removeChild(node);
6543
+ if (hook) emitDevtools(hook, "app:unmount", { rootElement: node });
6544
+ if (mounted) {
6545
+ unmountRange(mounted.start, mounted.end);
6546
+ return;
5569
6547
  }
6548
+ dispose(node);
6549
+ node.parentNode?.removeChild(node);
5570
6550
  }
5571
6551
  };
5572
6552
  }
6553
+ function unmountRange(start, end) {
6554
+ const parent = start.parentNode;
6555
+ if (!parent && !end.parentNode) return;
6556
+ let budget = MAX_DRAIN_TEARDOWNS;
6557
+ for (let n = start.nextSibling; n && n !== end; n = n.nextSibling) budget++;
6558
+ let before = start;
6559
+ let after = null;
6560
+ let intact = true;
6561
+ for (; ; ) {
6562
+ if (start.parentNode !== parent || end.parentNode !== parent) intact = false;
6563
+ else if (start.previousSibling !== before || end.nextSibling !== after) {
6564
+ before = start.previousSibling;
6565
+ after = end.nextSibling;
6566
+ intact = !!(start.compareDocumentPosition(end) & 4);
6567
+ }
6568
+ const n = start.nextSibling;
6569
+ if (!intact || n === end) break;
6570
+ if (budget-- <= 0) {
6571
+ reportDrainRunaway("mount", MAX_DRAIN_TEARDOWNS, 1);
6572
+ return;
6573
+ }
6574
+ dispose(n);
6575
+ if (n.parentNode === parent) parent.removeChild(n);
6576
+ }
6577
+ if (!intact) {
6578
+ reportError(new Error("[SibuJS mount] fragment markers lost"), { phase: "cleanup", name: "mount" });
6579
+ }
6580
+ start.parentNode?.removeChild(start);
6581
+ end.parentNode?.removeChild(end);
6582
+ }
5573
6583
 
5574
6584
  // src/core/rendering/portal.ts
5575
6585
  function Portal(nodes, target) {
@@ -5605,10 +6615,15 @@ function Portal(nodes, target) {
5605
6615
 
5606
6616
  // src/core/rendering/slots.ts
5607
6617
  function getSlot(slots, name = "default") {
5608
- return slots?.[name];
6618
+ if (slots == null || !Object.hasOwn(slots, name)) return void 0;
6619
+ const slot2 = slots[name];
6620
+ return typeof slot2 === "function" ? slot2 : void 0;
5609
6621
  }
5610
6622
 
5611
6623
  // src/core/signals/array.ts
6624
+ function nativeSplice(target, start, rest) {
6625
+ return rest.length === 0 ? target.splice(start) : target.splice(start, ...rest);
6626
+ }
5612
6627
  function array(initial = []) {
5613
6628
  const [arr, setArr] = signal([...initial]);
5614
6629
  const actions = {
@@ -5636,12 +6651,12 @@ function array(initial = []) {
5636
6651
  unshift(...items) {
5637
6652
  setArr((prev) => [...items, ...prev]);
5638
6653
  },
5639
- splice(start, deleteCount = 0, ...items) {
6654
+ splice(start, ...rest) {
5640
6655
  let removed = [];
5641
6656
  setArr((prev) => {
5642
6657
  const copy = [...prev];
5643
- removed = copy.splice(start, deleteCount, ...items);
5644
- return copy;
6658
+ removed = nativeSplice(copy, start, rest);
6659
+ return removed.length === 0 && rest.length <= 1 ? prev : copy;
5645
6660
  });
5646
6661
  return removed;
5647
6662
  },
@@ -5724,9 +6739,9 @@ function reactiveArray(initial = []) {
5724
6739
  data2.unshift(...items);
5725
6740
  notify();
5726
6741
  },
5727
- splice(start, deleteCount = 0, ...items) {
5728
- const removed = data2.splice(start, deleteCount, ...items);
5729
- if (removed.length > 0 || items.length > 0) {
6742
+ splice(start, ...rest) {
6743
+ const removed = nativeSplice(data2, start, rest);
6744
+ if (removed.length > 0 || rest.length > 1) {
5730
6745
  notify();
5731
6746
  }
5732
6747
  return removed;
@@ -5948,6 +6963,21 @@ function deepEqual(a2, b2, seen) {
5948
6963
  if (a2.length !== b2.length) return false;
5949
6964
  return a2.every((val, i2) => deepEqual(val, b2[i2], seen));
5950
6965
  }
6966
+ if (a2 instanceof Number || a2 instanceof String || a2 instanceof Boolean) {
6967
+ return Object.is(a2.valueOf(), b2.valueOf());
6968
+ }
6969
+ if (typeof URL !== "undefined" && a2 instanceof URL) {
6970
+ return a2.href === b2.href;
6971
+ }
6972
+ if (a2 instanceof Error) {
6973
+ const eb = b2;
6974
+ return a2.name === eb.name && a2.message === eb.message && deepEqual(a2.cause, eb.cause, seen) && plainKeysEqual(objA, objB, seen);
6975
+ }
6976
+ const proto = Object.getPrototypeOf(objA);
6977
+ if (proto !== Object.prototype && proto !== null) return false;
6978
+ return plainKeysEqual(objA, objB, seen);
6979
+ }
6980
+ function plainKeysEqual(objA, objB, seen) {
5951
6981
  const keysA = Object.keys(objA);
5952
6982
  const keysB = Object.keys(objB);
5953
6983
  if (keysA.length !== keysB.length) return false;
@@ -5985,17 +7015,17 @@ function derived(getter, options) {
5985
7015
  cs._d = false;
5986
7016
  cs._init = true;
5987
7017
  };
5988
- track2(() => {
5989
- let threw = true;
5990
- try {
7018
+ try {
7019
+ track2(() => {
5991
7020
  cs._v = getter();
5992
7021
  cs._d = false;
5993
7022
  cs._init = true;
5994
- threw = false;
5995
- } finally {
5996
- if (threw) cs._d = true;
5997
- }
5998
- }, markDirty);
7023
+ }, markDirty);
7024
+ } catch (err) {
7025
+ disposed = true;
7026
+ cleanup2(markDirty);
7027
+ throw err;
7028
+ }
5999
7029
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6000
7030
  const validate = () => {
6001
7031
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -6014,7 +7044,7 @@ function derived(getter, options) {
6014
7044
  if (disposed) cleanup2(markDirty);
6015
7045
  }
6016
7046
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
6017
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
7047
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
6018
7048
  }
6019
7049
  };
6020
7050
  cs._validate = validate;
@@ -6065,7 +7095,7 @@ function derived(getter, options) {
6065
7095
  }
6066
7096
  }
6067
7097
  };
6068
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
7098
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
6069
7099
  return computedGetter;
6070
7100
  }
6071
7101
 
@@ -6083,15 +7113,18 @@ function ref(initial) {
6083
7113
  }
6084
7114
 
6085
7115
  // src/core/signals/store.ts
7116
+ function defineOwn(obj, key, value) {
7117
+ Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
7118
+ }
6086
7119
  function store(initialState) {
6087
7120
  devAssert(
6088
7121
  initialState !== null && typeof initialState === "object" && !Array.isArray(initialState),
6089
7122
  "store: argument must be a plain object. For arrays, use array() instead."
6090
7123
  );
6091
- const signals = {};
7124
+ const signals = /* @__PURE__ */ Object.create(null);
6092
7125
  Object.keys(initialState).forEach((key) => {
6093
7126
  const [getter, setter] = signal(initialState[key]);
6094
- signals[key] = [getter, setter];
7127
+ defineOwn(signals, key, [getter, setter]);
6095
7128
  });
6096
7129
  const store2 = new Proxy({}, {
6097
7130
  get(target, prop) {
@@ -6110,7 +7143,7 @@ function store(initialState) {
6110
7143
  const getSnapshot = () => {
6111
7144
  const snapshot = {};
6112
7145
  Object.keys(signals).forEach((key) => {
6113
- snapshot[key] = signals[key][0]();
7146
+ defineOwn(snapshot, key, signals[key][0]());
6114
7147
  });
6115
7148
  return snapshot;
6116
7149
  };
@@ -6246,6 +7279,13 @@ function strictEffect(fn) {
6246
7279
  };
6247
7280
  }
6248
7281
 
7282
+ // src/reactivity/domBinding.ts
7283
+ function domBinding(commit, ownerNode) {
7284
+ if (isSSR()) return () => {
7285
+ };
7286
+ return reactiveBinding2(commit, ownerNode);
7287
+ }
7288
+
6249
7289
  // src/platform/enhance.ts
6250
7290
  var ENHANCED_ATTR = "data-sibu-enhanced";
6251
7291
  var enhancementOwner = /* @__PURE__ */ new WeakMap();
@@ -6281,9 +7321,7 @@ function resolveTarget(root, target) {
6281
7321
  }
6282
7322
  }
6283
7323
  function bindNode(el, commit) {
6284
- if (isSSR()) return () => {
6285
- };
6286
- return reactiveBinding2(commit, el);
7324
+ return domBinding(commit, el);
6287
7325
  }
6288
7326
  function readControlValue(el) {
6289
7327
  if (el instanceof HTMLInputElement) {
@@ -6541,12 +7579,6 @@ function enhanceAll(selector, setup) {
6541
7579
  };
6542
7580
  }
6543
7581
 
6544
- // src/utils/globalSingleton.ts
6545
- function globalSingleton(key, create) {
6546
- const g2 = globalThis;
6547
- return g2[key] ?? (g2[key] = create());
6548
- }
6549
-
6550
7582
  // src/platform/islands.ts
6551
7583
  var SAFE_NAME = /^[A-Za-z0-9_-]+$/;
6552
7584
  var LAZY = /* @__PURE__ */ Symbol.for("sibujs.islands.lazy");
@@ -6752,23 +7784,27 @@ function scheduleIdle(fn) {
6752
7784
  }
6753
7785
  function transition() {
6754
7786
  const [pending, setPending] = signal(false);
7787
+ let outstanding = 0;
6755
7788
  function start(fn) {
7789
+ outstanding++;
6756
7790
  setPending(true);
7791
+ let settled = false;
7792
+ const finish = () => {
7793
+ if (settled) return;
7794
+ settled = true;
7795
+ outstanding--;
7796
+ if (outstanding === 0) setPending(false);
7797
+ };
7798
+ const fail = (error) => {
7799
+ finish();
7800
+ reportError(error, { phase: "async", name: "transition" });
7801
+ };
6757
7802
  scheduleIdle(() => {
6758
- let result;
6759
7803
  try {
6760
- result = fn();
6761
- } catch {
6762
- setPending(false);
6763
- return;
6764
- }
6765
- if (result && typeof result.then === "function") {
6766
- result.then(
6767
- () => setPending(false),
6768
- () => setPending(false)
6769
- );
6770
- } else {
6771
- setPending(false);
7804
+ const adopted = adoptThenable(fn());
7805
+ adopted ? adopted.then(finish, fail) : finish();
7806
+ } catch (error) {
7807
+ fail(error);
6772
7808
  }
6773
7809
  });
6774
7810
  }
@@ -6789,7 +7825,23 @@ function nextTick() {
6789
7825
  }
6790
7826
 
6791
7827
  // src/build/cdn.ts
6792
- var PACKAGE_NAME = "sibu";
7828
+ var PACKAGE_NAME = "sibujs";
7829
+ var SUBPATH_ENTRIES = [
7830
+ "data",
7831
+ "browser",
7832
+ "patterns",
7833
+ "motion",
7834
+ "ui",
7835
+ "widgets",
7836
+ "ssr",
7837
+ "devtools",
7838
+ "performance",
7839
+ "ecosystem",
7840
+ "plugins",
7841
+ "build",
7842
+ "testing",
7843
+ "extras"
7844
+ ];
6793
7845
  function registerGlobal() {
6794
7846
  if (typeof window === "undefined") return;
6795
7847
  window.Sibu = { ...index_exports };
@@ -6835,7 +7887,7 @@ var cdnUrls = {
6835
7887
  * @example
6836
7888
  * ```ts
6837
7889
  * cdnUrls.scriptTag('jsdelivr', '1.0.0')
6838
- * // => '<script src="https://cdn.jsdelivr.net/npm/sibu@1.0.0/dist/cdn.global.js"></script>'
7890
+ * // => '<script src="https://cdn.jsdelivr.net/npm/sibujs@1.0.0/dist/cdn.global.js"></script>'
6839
7891
  * ```
6840
7892
  */
6841
7893
  scriptTag: (provider = "jsdelivr", version) => {
@@ -6853,15 +7905,11 @@ function generateImportMap(baseUrl) {
6853
7905
  const base2 = baseUrl || `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@latest`;
6854
7906
  const imports = {
6855
7907
  // Main package entry
6856
- [PACKAGE_NAME]: `${base2}/dist/index.js`,
6857
- // Sub-path imports for tree-shaking in browsers
6858
- [`${PACKAGE_NAME}/core`]: `${base2}/dist/core/index.js`,
6859
- [`${PACKAGE_NAME}/reactivity`]: `${base2}/dist/reactivity/index.js`,
6860
- [`${PACKAGE_NAME}/plugins`]: `${base2}/dist/plugins/index.js`,
6861
- [`${PACKAGE_NAME}/components`]: `${base2}/dist/components/index.js`,
6862
- [`${PACKAGE_NAME}/testing`]: `${base2}/dist/testing/index.js`,
6863
- [`${PACKAGE_NAME}/build`]: `${base2}/dist/build/index.js`
7908
+ [PACKAGE_NAME]: `${base2}/dist/index.js`
6864
7909
  };
7910
+ for (const entry of SUBPATH_ENTRIES) {
7911
+ imports[`${PACKAGE_NAME}/${entry}`] = `${base2}/dist/${entry}.js`;
7912
+ }
6865
7913
  return {
6866
7914
  imports,
6867
7915
  /**
@@ -6944,9 +7992,9 @@ function generateTsConfig(options) {
6944
7992
  if (paths && Object.keys(paths).length > 0) {
6945
7993
  compilerOptions.baseUrl = ".";
6946
7994
  compilerOptions.paths = {
6947
- // Default sibu alias
6948
- sibu: ["node_modules/sibu/dist/index.d.ts"],
6949
- "sibu/*": ["node_modules/sibu/dist/*"],
7995
+ // Default sibujs alias
7996
+ sibujs: ["node_modules/sibujs/dist/index.d.ts"],
7997
+ "sibujs/*": ["node_modules/sibujs/dist/*"],
6950
7998
  // User-provided paths
6951
7999
  ...paths
6952
8000
  };
@@ -8060,6 +9108,19 @@ function generateTypeStubs() {
8060
9108
  };
8061
9109
  }
8062
9110
 
9111
+ // src/build/compileTemplates.ts
9112
+ function compileHtmlTemplates(code2) {
9113
+ const plan = planHtmlTemplates(code2);
9114
+ const edits = plan.compiledCount > 0 ? normalizeEdits(plan.edits) : null;
9115
+ return {
9116
+ code: edits ? applyEdits(code2, edits, plan.append) : null,
9117
+ usedTags: plan.usedTags,
9118
+ usesSvg: plan.usesSvg,
9119
+ compiledCount: edits ? plan.compiledCount : 0,
9120
+ skippedCount: plan.skippedCount
9121
+ };
9122
+ }
9123
+
8063
9124
  // src/build/routeSplitting.ts
8064
9125
  function fileToRoute(relativePath) {
8065
9126
  const basename = relativePath.split("/").pop() || "";
@@ -8115,7 +9176,7 @@ function generateRouteModule(entries, routesDir) {
8115
9176
  (e, i2) => `const route${i2} = () => import(/* webpackChunkName: "${e.chunkName}" */ "${routesDir}/${e.importPath}");`
8116
9177
  ).join("\n");
8117
9178
  const routes = entries.map((e, i2) => ` { path: "${e.path}", component: lazy(route${i2}) }`).join(",\n");
8118
- return `import { lazy } from "sibu";
9179
+ return `import { lazy } from "sibujs";
8119
9180
 
8120
9181
  ${imports}
8121
9182