sibujs 4.6.0 → 4.8.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 (94) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +62 -8
  3. package/dist/browser.d.cts +23 -0
  4. package/dist/browser.d.ts +23 -0
  5. package/dist/browser.js +6 -5
  6. package/dist/build.cjs +2388 -1004
  7. package/dist/build.d.cts +147 -44
  8. package/dist/build.d.ts +147 -44
  9. package/dist/build.js +1410 -564
  10. package/dist/cdn.dev.global.js +14 -12
  11. package/dist/cdn.full.dev.global.js +15 -13
  12. package/dist/cdn.full.global.js +14 -12
  13. package/dist/cdn.global.js +12 -10
  14. package/dist/chunk-3ACAAQIN.js +224 -0
  15. package/dist/{chunk-7NCARGJW.js → chunk-44MTL7ED.js} +6 -6
  16. package/dist/{chunk-BAAG6ZTI.js → chunk-4WMSTZ7J.js} +8 -5
  17. package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
  18. package/dist/{chunk-DKGBBKOF.js → chunk-C5PEWEGH.js} +20 -5
  19. package/dist/{chunk-3ISI6ACU.js → chunk-C7AA2CZX.js} +1 -1
  20. package/dist/{chunk-UZQ6ALFS.js → chunk-CZPRSWSS.js} +1 -1
  21. package/dist/{chunk-D33YTSX5.js → chunk-DSID757X.js} +5 -4
  22. package/dist/{chunk-OXUY2A6L.js → chunk-EGUQLBHE.js} +22 -8
  23. package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
  24. package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
  25. package/dist/{chunk-HYXDKS4N.js → chunk-GSGNX35Q.js} +5 -5
  26. package/dist/{chunk-7GQCWFOE.js → chunk-J3YQ457H.js} +171 -85
  27. package/dist/{chunk-ORMZXBKQ.js → chunk-JUTFX2RM.js} +18 -10
  28. package/dist/{chunk-QE4TTDU3.js → chunk-KJ3UKABJ.js} +12 -11
  29. package/dist/{chunk-XRRZKZYX.js → chunk-LI4W3U6C.js} +1 -1
  30. package/dist/{chunk-47M47FOM.js → chunk-O6TRVMJI.js} +58 -6
  31. package/dist/{chunk-JSXPZCET.js → chunk-Q5J2LVKH.js} +1 -1
  32. package/dist/{chunk-5MT6SJ3P.js → chunk-QGMSESDD.js} +10 -5
  33. package/dist/{chunk-AIF3Z2T7.js → chunk-RGLPEBDE.js} +13 -10
  34. package/dist/{chunk-5Q4R7HCL.js → chunk-TQQUN4WR.js} +11 -2
  35. package/dist/{chunk-PK6FK2G2.js → chunk-UCNP3HVM.js} +183 -30
  36. package/dist/{chunk-XC4MEKGA.js → chunk-V3LHZSU5.js} +8 -2
  37. package/dist/{chunk-VVWPJ543.js → chunk-VWT3VXPL.js} +55 -20
  38. package/dist/{chunk-SXXVZMKZ.js → chunk-ZVLHNEEL.js} +17 -35
  39. package/dist/{contracts-DRIuclVT.d.cts → contracts-CLqzJnOV.d.ts} +28 -17
  40. package/dist/{contracts-DRIuclVT.d.ts → contracts-CTOJXu-x.d.cts} +28 -17
  41. package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
  42. package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
  43. package/dist/data.cjs +205 -39
  44. package/dist/data.d.cts +119 -3
  45. package/dist/data.d.ts +119 -3
  46. package/dist/data.js +6 -6
  47. package/dist/devtools.cjs +18 -5
  48. package/dist/devtools.js +5 -5
  49. package/dist/ecosystem.cjs +462 -58
  50. package/dist/ecosystem.d.cts +2 -1
  51. package/dist/ecosystem.d.ts +2 -1
  52. package/dist/ecosystem.js +9 -8
  53. package/dist/extras.cjs +852 -296
  54. package/dist/extras.d.cts +5 -4
  55. package/dist/extras.d.ts +5 -4
  56. package/dist/extras.js +24 -23
  57. package/dist/index.cjs +721 -176
  58. package/dist/index.d.cts +220 -169
  59. package/dist/index.d.ts +220 -169
  60. package/dist/index.js +16 -12
  61. package/dist/motion.cjs +12 -4
  62. package/dist/motion.js +3 -3
  63. package/dist/patterns.cjs +37 -21
  64. package/dist/patterns.d.cts +2 -1
  65. package/dist/patterns.d.ts +2 -1
  66. package/dist/patterns.js +6 -6
  67. package/dist/performance.cjs +48 -6
  68. package/dist/performance.d.cts +12 -2
  69. package/dist/performance.d.ts +12 -2
  70. package/dist/performance.js +8 -6
  71. package/dist/plugins.cjs +672 -261
  72. package/dist/plugins.d.cts +131 -13
  73. package/dist/plugins.d.ts +131 -13
  74. package/dist/plugins.js +83 -47
  75. package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
  76. package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.cts} +6 -0
  77. package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.ts} +6 -0
  78. package/dist/ssr.cjs +509 -142
  79. package/dist/ssr.d.cts +55 -5
  80. package/dist/ssr.d.ts +55 -5
  81. package/dist/ssr.js +11 -9
  82. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  83. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  84. package/dist/testing.cjs +17 -4
  85. package/dist/testing.js +4 -4
  86. package/dist/types-CJFViL6Q.d.cts +26 -0
  87. package/dist/types-CJFViL6Q.d.ts +26 -0
  88. package/dist/ui.cjs +635 -254
  89. package/dist/ui.d.cts +3 -2
  90. package/dist/ui.d.ts +3 -2
  91. package/dist/ui.js +9 -8
  92. package/dist/widgets.cjs +22 -14
  93. package/dist/widgets.js +7 -7
  94. package/package.json +8 -5
package/dist/build.cjs CHANGED
@@ -59,8 +59,1310 @@ __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
+ var ASCII_UPPER = /[A-Z]/;
795
+ var ASCII_UPPER_G = /[A-Z]/g;
796
+ function canonicalAttrName(name) {
797
+ return ASCII_UPPER.test(name) ? name.replace(ASCII_UPPER_G, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : name;
798
+ }
799
+ function isEventHandlerAttr(name) {
800
+ if (name.length < 3) return false;
801
+ const lower = name.toLowerCase();
802
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
803
+ }
804
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
805
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
806
+ function sanitizeUrl(url) {
807
+ const probe = stripControlChars(url).trim();
808
+ if (!probe) return "";
809
+ const output2 = url.replace(OUTER_TRIM, "");
810
+ if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
811
+ return "";
812
+ }
813
+ const lower = probe.toLowerCase();
814
+ let schemeEnd = -1;
815
+ for (let i2 = 0; i2 < lower.length; i2++) {
816
+ const ch = lower.charCodeAt(i2);
817
+ if (ch === 58) {
818
+ schemeEnd = i2;
819
+ break;
820
+ }
821
+ if (ch === 47 || ch === 63 || ch === 35) break;
822
+ }
823
+ if (schemeEnd === -1) return output2;
824
+ const scheme = lower.slice(0, schemeEnd + 1);
825
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
826
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
827
+ return output2;
828
+ }
829
+ var SRCSET_DESCRIPTOR = /^\d+(\.\d+)?[wx]$/;
830
+ function isSrcsetWhitespace(ch) {
831
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
832
+ }
833
+ function sanitizeSrcset(value) {
834
+ const input2 = String(value);
835
+ const out = [];
836
+ let i2 = 0;
837
+ const n = input2.length;
838
+ while (i2 < n) {
839
+ while (i2 < n && (isSrcsetWhitespace(input2[i2]) || input2[i2] === ",")) i2++;
840
+ if (i2 >= n) break;
841
+ const urlStart = i2;
842
+ while (i2 < n && !isSrcsetWhitespace(input2[i2])) i2++;
843
+ let url = input2.slice(urlStart, i2);
844
+ const descriptors = [];
845
+ if (url.endsWith(",")) {
846
+ url = url.replace(/,+$/, "");
847
+ } else {
848
+ let current = "";
849
+ let inParens = false;
850
+ while (i2 < n) {
851
+ const ch = input2[i2];
852
+ if (inParens) {
853
+ current += ch;
854
+ if (ch === ")") inParens = false;
855
+ i2++;
856
+ continue;
857
+ }
858
+ if (isSrcsetWhitespace(ch)) {
859
+ if (current) descriptors.push(current);
860
+ current = "";
861
+ i2++;
862
+ continue;
863
+ }
864
+ if (ch === ",") {
865
+ i2++;
866
+ break;
867
+ }
868
+ if (ch === "(") inParens = true;
869
+ current += ch;
870
+ i2++;
871
+ }
872
+ if (current) descriptors.push(current);
873
+ }
874
+ if (url === "") continue;
875
+ if (descriptors.length > 1) continue;
876
+ if (descriptors.length === 1 && !SRCSET_DESCRIPTOR.test(descriptors[0])) continue;
877
+ const safe = sanitizeUrl(url);
878
+ if (!safe) continue;
879
+ if (safe.startsWith(",") || safe.endsWith(",")) continue;
880
+ out.push(descriptors.length === 1 ? `${safe} ${descriptors[0]}` : safe);
881
+ }
882
+ return out.join(", ");
883
+ }
884
+ var CSS_DANGER_GATE = /[(:@\\]/;
885
+ function isCssWhitespace(ch) {
886
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
887
+ }
888
+ function isHexDigit(ch) {
889
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
890
+ }
891
+ function decodeCssEscapes(value) {
892
+ let out = "";
893
+ for (let i2 = 0; i2 < value.length; i2++) {
894
+ const ch = value[i2];
895
+ if (ch !== "\\") {
896
+ out += ch;
897
+ continue;
898
+ }
899
+ if (i2 + 1 >= value.length) break;
900
+ const next = value[i2 + 1];
901
+ if (next === "\n" || next === "\f") {
902
+ i2 += 1;
903
+ continue;
904
+ }
905
+ if (next === "\r") {
906
+ i2 += value[i2 + 2] === "\n" ? 2 : 1;
907
+ continue;
908
+ }
909
+ if (isHexDigit(next)) {
910
+ let hex = "";
911
+ let j = i2 + 1;
912
+ while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
913
+ hex += value[j];
914
+ j++;
915
+ }
916
+ if (j < value.length && isCssWhitespace(value[j])) {
917
+ j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
918
+ }
919
+ const code2 = Number.parseInt(hex, 16);
920
+ const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
921
+ out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
922
+ i2 = j - 1;
923
+ continue;
924
+ }
925
+ out += next;
926
+ i2 += 1;
927
+ }
928
+ return out;
929
+ }
930
+ var BLOCKED_CSS_NEEDLES = [
931
+ "url(",
932
+ "expression(",
933
+ "javascript:",
934
+ "vbscript:",
935
+ "-moz-binding",
936
+ "behavior:",
937
+ "@import",
938
+ "image-set(",
939
+ "filter:progid",
940
+ // CSS Images 4 `image("…")` and CSS Values 5 `src("…")` take a URL as a
941
+ // plain STRING, so they fetch without ever spelling `url(`. Support is thin
942
+ // today, which is exactly when a blocklist should get ahead of it.
943
+ "image(",
944
+ "src("
945
+ ];
946
+ var CSS_REASON_MOZ_BINDING = 4;
947
+ var CSS_REASON_BEHAVIOR = 5;
948
+ var CSS_REASON_PROGID = 8;
949
+ function describeElement(el) {
950
+ if (!el) return "an element";
951
+ let out = el.tagName.toLowerCase();
952
+ const id = el.getAttribute("id");
953
+ if (id) out += `#${id}`;
954
+ const cls = el.getAttribute("class");
955
+ if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
956
+ return `<${out}>`;
957
+ }
958
+ var warnedDeclarations = /* @__PURE__ */ new Set();
959
+ var MAX_WARNED_DECLARATIONS = 100;
960
+ var declarationCapAnnounced = false;
961
+ function warnDroppedDeclaration(value, reason, ctx) {
962
+ devWarnLazy(() => {
963
+ const reasons = [
964
+ "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",
965
+ "expression() executes arbitrary script in legacy engines",
966
+ "the javascript: scheme executes script",
967
+ "the vbscript: scheme executes script",
968
+ "-moz-binding loads and runs remote XBL",
969
+ "behavior: loads and runs a remote HTC script",
970
+ "@import pulls in a remote stylesheet",
971
+ "image-set() fetches remote images, the same exfiltration channel as url()",
972
+ "filter:progid activates legacy scriptable filters",
973
+ "image() fetches its string argument as a URL, the same exfiltration channel as url()",
974
+ "src() fetches its string argument as a URL, the same exfiltration channel as url()"
975
+ ];
976
+ const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
977
+ const where = describeElement(ctx?.element);
978
+ const key = `${where}|${declaration}`;
979
+ if (warnedDeclarations.has(key)) return "";
980
+ if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
981
+ if (declarationCapAnnounced) return "";
982
+ declarationCapAnnounced = true;
983
+ 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.`;
984
+ }
985
+ warnedDeclarations.add(key);
986
+ 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.`;
987
+ });
988
+ }
989
+ function normalizeCssForScan(value) {
990
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
991
+ return normalized.toLowerCase().replace(/\s+/g, "");
992
+ }
993
+ function blockedCssReason(value) {
994
+ if (!CSS_DANGER_GATE.test(value)) return -1;
995
+ const lower = normalizeCssForScan(value);
996
+ for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
997
+ if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
998
+ }
999
+ return -1;
1000
+ }
1001
+ function sanitizeCSSDeclaration(property, value, context2) {
1002
+ const reason = blockedDeclarationReason(property, value);
1003
+ if (reason === -1) return value;
1004
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1005
+ return "";
1006
+ }
1007
+ function blockedDeclarationReason(property, value) {
1008
+ const reason = blockedCssReason(value);
1009
+ if (reason !== -1) return reason;
1010
+ const name = canonicalAttrName(property);
1011
+ if (name === "behavior") return CSS_REASON_BEHAVIOR;
1012
+ if (name === "-moz-binding") return CSS_REASON_MOZ_BINDING;
1013
+ if ((name === "filter" || name === "-ms-filter") && normalizeCssForScan(value).includes("progid:")) {
1014
+ return CSS_REASON_PROGID;
1015
+ }
1016
+ return -1;
1017
+ }
1018
+ function parseStyleDeclarations(css) {
1019
+ const chunks = [];
1020
+ let buf = "";
1021
+ let depth = 0;
1022
+ let quote2 = "";
1023
+ for (let i2 = 0; i2 < css.length; i2++) {
1024
+ const ch = css[i2];
1025
+ if (quote2) {
1026
+ buf += ch;
1027
+ if (ch === "\\" && i2 + 1 < css.length) buf += css[++i2];
1028
+ else if (ch === quote2) quote2 = "";
1029
+ continue;
1030
+ }
1031
+ if (ch === "\\") {
1032
+ buf += ch;
1033
+ if (i2 + 1 < css.length) buf += css[++i2];
1034
+ continue;
1035
+ }
1036
+ if (ch === "/" && css[i2 + 1] === "*") {
1037
+ const end = css.indexOf("*/", i2 + 2);
1038
+ if (end === -1) return null;
1039
+ i2 = end + 1;
1040
+ continue;
1041
+ }
1042
+ if (ch === '"' || ch === "'") quote2 = ch;
1043
+ else if (ch === "(" || ch === "[") depth++;
1044
+ else if (ch === ")" || ch === "]") {
1045
+ if (--depth < 0) return null;
1046
+ } else if (ch === "{" || ch === "}") return null;
1047
+ else if (ch === ";" && depth === 0) {
1048
+ chunks.push(buf);
1049
+ buf = "";
1050
+ continue;
1051
+ }
1052
+ buf += ch;
1053
+ }
1054
+ if (quote2 || depth !== 0) return null;
1055
+ chunks.push(buf);
1056
+ const out = [];
1057
+ for (const chunk of chunks) {
1058
+ const colon = chunk.indexOf(":");
1059
+ if (colon === -1) continue;
1060
+ const rawName = chunk.slice(0, colon).trim();
1061
+ if (!/^(--[\w-]+|-?[a-zA-Z_][\w-]*)$/.test(rawName)) continue;
1062
+ let value = chunk.slice(colon + 1).trim();
1063
+ const bang = /!\s*important\s*$/i.exec(value);
1064
+ if (bang) value = value.slice(0, bang.index).trim();
1065
+ if (value === "") continue;
1066
+ const property = rawName.startsWith("--") ? rawName : canonicalAttrName(rawName);
1067
+ out.push([property, value, bang !== null]);
1068
+ }
1069
+ return out;
1070
+ }
1071
+ function sanitizeStyleDeclarationList(cssText, context2) {
1072
+ const input2 = String(cssText);
1073
+ const declarations = parseStyleDeclarations(input2);
1074
+ if (declarations === null) {
1075
+ if (blockedCssReason(input2) !== -1) warnDroppedDeclaration(input2, blockedCssReason(input2), context2);
1076
+ return "";
1077
+ }
1078
+ const kept = [];
1079
+ for (const [property, value, important] of declarations) {
1080
+ const reason = blockedDeclarationReason(property, value);
1081
+ if (reason !== -1) {
1082
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1083
+ continue;
1084
+ }
1085
+ kept.push(`${property}: ${value}${important ? " !important" : ""}`);
1086
+ }
1087
+ return kept.join("; ");
1088
+ }
1089
+ function sanitizeStyleAttribute(cssText, context2) {
1090
+ const input2 = String(cssText);
1091
+ if (input2.trim() === "") return "";
1092
+ if (typeof document === "undefined") return sanitizeStyleDeclarationList(input2, context2);
1093
+ const probe = document.createElement("div");
1094
+ try {
1095
+ probe.style.cssText = input2;
1096
+ } catch {
1097
+ return "";
1098
+ }
1099
+ const declarations = [];
1100
+ for (let i2 = 0; i2 < probe.style.length; i2++) {
1101
+ const property = probe.style[i2];
1102
+ const value = probe.style.getPropertyValue(property);
1103
+ const reason = blockedDeclarationReason(property, value);
1104
+ if (reason !== -1) {
1105
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1106
+ continue;
1107
+ }
1108
+ const priority = probe.style.getPropertyPriority(property);
1109
+ declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
1110
+ }
1111
+ return declarations.join("; ");
1112
+ }
1113
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1114
+ "href",
1115
+ "xlink:href",
1116
+ "src",
1117
+ "action",
1118
+ "formaction",
1119
+ "formtarget",
1120
+ "cite",
1121
+ "poster",
1122
+ "background",
1123
+ "srcset",
1124
+ "ping",
1125
+ "data",
1126
+ // `<html manifest>` is a URL sink too. It was carried only by the SSR
1127
+ // serializers' private copy of this set; folding that copy into this one would
1128
+ // otherwise have silently dropped its coverage.
1129
+ "manifest"
1130
+ ]);
1131
+ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
1132
+ function isHtmlContentAttribute(name) {
1133
+ return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
1134
+ }
1135
+ function isUrlAttribute(attr) {
1136
+ return URL_ATTRIBUTES.has(attr.toLowerCase());
1137
+ }
1138
+ function isPolicyAttribute(name) {
1139
+ const lower = name.toLowerCase();
1140
+ return lower === "style" || URL_ATTRIBUTES.has(lower);
1141
+ }
1142
+ function sanitizeAttributeString(attr, value, context2) {
1143
+ const lower = attr.toLowerCase();
1144
+ if (lower === "srcset") return sanitizeSrcset(value);
1145
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1146
+ if (lower === "style") return sanitizeStyleAttribute(value, context2);
1147
+ return value;
1148
+ }
1149
+ function resolveAttributeValue(attr, value, context2) {
1150
+ if (!isPolicyAttribute(attr)) return value;
1151
+ const safe = sanitizeAttributeString(attr, value, context2);
1152
+ return safe === "" ? null : safe;
1153
+ }
1154
+
1155
+ // src/build/staticAnalysis.ts
1156
+ var STATIC_TAGS = /* @__PURE__ */ new Set([
1157
+ "div",
1158
+ "span",
1159
+ "section",
1160
+ "article",
1161
+ "header",
1162
+ "footer",
1163
+ "nav",
1164
+ "main",
1165
+ "aside",
1166
+ "p",
1167
+ "h1",
1168
+ "h2",
1169
+ "h3",
1170
+ "h4",
1171
+ "h5",
1172
+ "h6",
1173
+ "blockquote",
1174
+ "li",
1175
+ "ol",
1176
+ "ul",
1177
+ "a",
1178
+ "b",
1179
+ "em",
1180
+ "i",
1181
+ "strong",
1182
+ "small",
1183
+ "code",
1184
+ "mark",
1185
+ "img",
1186
+ "br",
1187
+ "hr",
1188
+ "input",
1189
+ "button",
1190
+ "form",
1191
+ "label",
1192
+ "details",
1193
+ "summary",
1194
+ "dialog"
1195
+ ]);
63
1196
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1197
+ "br",
1198
+ "hr",
1199
+ "img",
1200
+ "input",
1201
+ "meta",
1202
+ "link",
1203
+ "area",
1204
+ "base",
1205
+ "col",
1206
+ "embed",
1207
+ "param",
1208
+ "source",
1209
+ "track",
1210
+ "wbr"
1211
+ ]);
1212
+ var SPECIAL_PROPS = /* @__PURE__ */ new Set(["style", "ref", "on", "onElement"]);
1213
+ var NON_REFLECTING_BOOLEANS = /* @__PURE__ */ new Set(["checked", "selected"]);
1214
+ function readLiteral(code2, tok) {
1215
+ if (!tok) return null;
1216
+ if (tok.type === "string") {
1217
+ const value = stringTokenValue(code2, tok);
1218
+ return value === null ? null : { kind: "string", value };
1219
+ }
1220
+ if (tok.type === "number" && /^(0|[1-9]\d*)(\.\d+)?$/.test(tok.value)) {
1221
+ return { kind: "number", value: String(Number(tok.value)) };
1222
+ }
1223
+ if (tok.type === "ident" && ["true", "false", "null", "undefined"].includes(tok.value)) {
1224
+ return { kind: "keyword", value: tok.value };
1225
+ }
1226
+ return null;
1227
+ }
1228
+ function parserSafeText(s2) {
1229
+ return !s2.includes("\r") && !s2.includes("\0");
1230
+ }
1231
+ function escapeHtml(str) {
1232
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1233
+ }
1234
+ function escapeAttr(str) {
1235
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1236
+ }
1237
+ function attributeHtml(name, lit) {
1238
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) return null;
1239
+ const lower = name.toLowerCase();
1240
+ if (SPECIAL_PROPS.has(name) || lower === "style" || lower === "srcset") return null;
1241
+ if (isEventHandlerAttr(name) || isUrlAttribute(name) || isHtmlContentAttribute(name)) return null;
1242
+ if (lit.kind === "keyword") {
1243
+ if (lit.value === "null" || lit.value === "undefined") return "";
1244
+ const on2 = lit.value === "true";
1245
+ if (lower.startsWith("aria-")) return `${lower}="${on2}"`;
1246
+ if (NON_REFLECTING_BOOLEANS.has(lower)) return null;
1247
+ return on2 ? `${lower}=""` : "";
1248
+ }
1249
+ if (!parserSafeText(lit.value)) return null;
1250
+ return `${lower}="${escapeAttr(lit.value)}"`;
1251
+ }
1252
+ function parseCall(code2, list, i2) {
1253
+ const at = (k2) => list[k2];
1254
+ const isPunct = (k2, v) => {
1255
+ const t = at(k2);
1256
+ return !!t && t.type === "punct" && t.value === v;
1257
+ };
1258
+ let k = i2 + 1;
1259
+ const result = { end: 0, cls: null, id: null, attrs: [], text: null };
1260
+ let nodesText = null;
1261
+ if (isPunct(k, "{")) {
1262
+ k++;
1263
+ const seen = /* @__PURE__ */ new Set();
1264
+ while (!isPunct(k, "}")) {
1265
+ const keyTok = at(k);
1266
+ let key = null;
1267
+ if (keyTok?.type === "ident") key = keyTok.value;
1268
+ else if (keyTok?.type === "string") key = stringTokenValue(code2, keyTok);
1269
+ if (key === null || !isPunct(k + 1, ":")) return null;
1270
+ if (seen.has(key.toLowerCase())) return null;
1271
+ seen.add(key.toLowerCase());
1272
+ const lit = readLiteral(code2, at(k + 2));
1273
+ if (lit === null) return null;
1274
+ k += 3;
1275
+ if (isPunct(k, ",")) k++;
1276
+ else if (!isPunct(k, "}")) return null;
1277
+ if (key === "class" || key === "id") {
1278
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1279
+ if (key === "class") result.cls = lit.value;
1280
+ else result.id = lit.value;
1281
+ } else if (key === "nodes") {
1282
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1283
+ nodesText = lit.value;
1284
+ } else {
1285
+ const html3 = attributeHtml(key, lit);
1286
+ if (html3 === null) return null;
1287
+ if (html3) result.attrs.push(html3);
1288
+ }
1289
+ }
1290
+ k++;
1291
+ if (isPunct(k, ",")) {
1292
+ const second = readLiteral(code2, at(k + 1));
1293
+ if (second === null) {
1294
+ if (!isPunct(k + 1, ")")) return null;
1295
+ k++;
1296
+ } else {
1297
+ if (second.kind !== "string" || !parserSafeText(second.value)) return null;
1298
+ result.text = second.value;
1299
+ k += 2;
1300
+ if (isPunct(k, ",")) k++;
1301
+ }
1302
+ }
1303
+ if (result.text === null) result.text = nodesText;
1304
+ } else {
1305
+ const first = readLiteral(code2, at(k));
1306
+ if (first === null || first.kind !== "string" || !parserSafeText(first.value)) return null;
1307
+ k++;
1308
+ if (isPunct(k, ",") && at(k + 1)?.type === "string") {
1309
+ const second = readLiteral(code2, at(k + 1));
1310
+ if (second === null || second.kind !== "string" || !parserSafeText(second.value)) return null;
1311
+ result.cls = first.value;
1312
+ result.text = second.value;
1313
+ k += 2;
1314
+ } else {
1315
+ result.text = first.value;
1316
+ }
1317
+ if (isPunct(k, ",")) k++;
1318
+ }
1319
+ if (!isPunct(k, ")")) return null;
1320
+ result.end = at(k).end;
1321
+ return result;
1322
+ }
1323
+ function toHtml(tag, call) {
1324
+ const attrs = [];
1325
+ if (call.cls !== null) attrs.push(`class="${escapeAttr(call.cls)}"`);
1326
+ if (call.id !== null) attrs.push(`id="${escapeAttr(call.id)}"`);
1327
+ attrs.push(...call.attrs);
1328
+ const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
1329
+ if (VOID_ELEMENTS.has(tag)) {
1330
+ if (call.text) return null;
1331
+ return `<${tag}${attrStr} />`;
1332
+ }
1333
+ return `<${tag}${attrStr}>${escapeHtml(call.text ?? "")}</${tag}>`;
1334
+ }
1335
+ function analyzeStaticTemplates(code2) {
1336
+ const none = { hasStaticPatterns: false, patterns: [] };
1337
+ if (!code2.includes("sibujs")) return none;
1338
+ const scan = scanModule(code2);
1339
+ if (!scan) return none;
1340
+ const factories = /* @__PURE__ */ new Map();
1341
+ for (const decl of scan.imports) {
1342
+ if (decl.source !== "sibujs") continue;
1343
+ for (const b2 of decl.bindings) if (STATIC_TAGS.has(b2.imported)) factories.set(b2.local, b2.imported);
1344
+ }
1345
+ if (factories.size === 0) return none;
1346
+ const uses = bindingUses(code2, scan, new Set(factories.keys()));
1347
+ const unsafe = new Set(uses.shadowed);
1348
+ for (const ref2 of uses.tagged) unsafe.add(ref2.tok.value);
1349
+ const patterns = [];
1350
+ for (const { tok, list, index } of uses.calls) {
1351
+ if (unsafe.has(tok.value)) continue;
1352
+ const tag = factories.get(tok.value);
1353
+ const call = parseCall(code2, list, index + 1);
1354
+ if (!call) continue;
1355
+ const templateHtml = toHtml(tag, call);
1356
+ if (templateHtml === null) continue;
1357
+ const start = list[index].start;
1358
+ patterns.push({ original: code2.slice(start, call.end), tag, templateHtml, start, end: call.end });
1359
+ }
1360
+ return { hasStaticPatterns: patterns.length > 0, patterns };
1361
+ }
1362
+
1363
+ // src/build/templateCompiler.ts
1364
+ var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
1365
+ var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
64
1366
  "area",
65
1367
  "base",
66
1368
  "br",
@@ -99,96 +1401,10 @@ var SVG_TAGS = /* @__PURE__ */ new Set([
99
1401
  "symbol",
100
1402
  "marker"
101
1403
  ]);
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) {
1404
+ var SVG_NS = "http://www.w3.org/2000/svg";
1405
+ var RuntimeParseError = class extends Error {
1406
+ };
1407
+ function parseTemplate(strings) {
192
1408
  const exprCount = strings.length - 1;
193
1409
  let template2 = strings[0];
194
1410
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -197,26 +1413,34 @@ function parseTemplateToAST(strings) {
197
1413
  let pos = 0;
198
1414
  const len = template2.length;
199
1415
  function skipWs() {
200
- while (pos < len && /\s/.test(template2[pos])) pos++;
1416
+ while (pos < len && (template2[pos] === " " || template2[pos] === " " || template2[pos] === "\n" || template2[pos] === "\r"))
1417
+ pos++;
201
1418
  }
202
1419
  function tryExprIdx() {
203
- if (template2[pos] !== "\0") return -1;
1420
+ if (template2.charCodeAt(pos) !== 0) return -1;
204
1421
  const start = pos;
205
1422
  pos++;
206
- let idxStr = "";
207
- while (pos < len && template2[pos] !== "\0") idxStr += template2[pos++];
1423
+ let idx = 0;
1424
+ while (pos < len && template2.charCodeAt(pos) !== 0) {
1425
+ idx = idx * 10 + (template2.charCodeAt(pos) - 48);
1426
+ pos++;
1427
+ }
208
1428
  if (pos < len) pos++;
209
- const idx = Number.parseInt(idxStr, 10);
210
- if (Number.isNaN(idx) || idx < 0 || idx >= exprCount) {
1429
+ if (idx < 0 || idx >= exprCount) {
211
1430
  pos = start;
212
1431
  return -1;
213
1432
  }
214
1433
  return idx;
215
1434
  }
216
1435
  function readTagName() {
217
- let name = "";
218
- while (pos < len && /[a-zA-Z0-9-]/.test(template2[pos])) name += template2[pos++];
219
- return name;
1436
+ const start = pos;
1437
+ while (pos < len) {
1438
+ const c = template2.charCodeAt(pos);
1439
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45) {
1440
+ pos++;
1441
+ } else break;
1442
+ }
1443
+ return template2.slice(start, pos);
220
1444
  }
221
1445
  function parseAttrValue() {
222
1446
  skipWs();
@@ -224,51 +1448,72 @@ function parseTemplateToAST(strings) {
224
1448
  pos++;
225
1449
  skipWs();
226
1450
  const exprIdx = tryExprIdx();
227
- if (exprIdx >= 0) return { kind: "expr", index: exprIdx };
1451
+ if (exprIdx >= 0) return { kind: "expr", idx: exprIdx };
228
1452
  const quote2 = template2[pos];
229
1453
  if (quote2 === '"' || quote2 === "'") {
230
1454
  pos++;
231
- const parts = [];
232
- let current = "";
1455
+ const statics2 = [];
1456
+ const exprs2 = [];
1457
+ let current2 = "";
233
1458
  while (pos < len && template2[pos] !== quote2) {
234
1459
  const innerIdx = tryExprIdx();
235
1460
  if (innerIdx >= 0) {
236
- if (current) parts.push({ kind: "static", value: current });
237
- current = "";
238
- parts.push({ kind: "expr", index: innerIdx });
1461
+ statics2.push(current2);
1462
+ current2 = "";
1463
+ exprs2.push(innerIdx);
239
1464
  } else {
240
- current += template2[pos++];
1465
+ current2 += template2[pos++];
241
1466
  }
242
1467
  }
243
1468
  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 };
1469
+ statics2.push(current2);
1470
+ if (exprs2.length === 0) return { kind: "static", value: statics2[0] };
1471
+ return { kind: "mixed", statics: statics2, exprs: exprs2 };
1472
+ }
1473
+ const statics = [];
1474
+ const exprs = [];
1475
+ let current = "";
1476
+ while (pos < len) {
1477
+ const c = template2.charCodeAt(pos);
1478
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 62) break;
1479
+ const innerIdx = tryExprIdx();
1480
+ if (innerIdx >= 0) {
1481
+ statics.push(current);
1482
+ current = "";
1483
+ exprs.push(innerIdx);
1484
+ } else {
1485
+ current += template2[pos++];
250
1486
  }
251
- return { kind: "static", value: parts.map((p2) => p2.value).join("") };
252
1487
  }
253
- let val = "";
254
- while (pos < len && !/[\s>/]/.test(template2[pos])) val += template2[pos++];
255
- return { kind: "static", value: val };
1488
+ statics.push(current);
1489
+ if (exprs.length === 0) return { kind: "static", value: statics[0] };
1490
+ return { kind: "mixed", statics, exprs };
256
1491
  }
257
1492
  function parseAttrs() {
258
1493
  const attrs = [];
259
1494
  while (pos < len) {
260
1495
  skipWs();
261
1496
  if (template2[pos] === ">" || template2[pos] === "/") break;
262
- let attrName = "";
263
- while (pos < len && /[a-zA-Z0-9\-:_.]/.test(template2[pos])) attrName += template2[pos++];
1497
+ const attrStart = pos;
1498
+ while (pos < len) {
1499
+ const c = template2.charCodeAt(pos);
1500
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45 || c === 58 || c === 95 || c === 46) {
1501
+ pos++;
1502
+ } else break;
1503
+ }
1504
+ const attrName = template2.slice(attrStart, pos);
264
1505
  if (!attrName) break;
265
1506
  const val = parseAttrValue();
266
1507
  if (attrName.startsWith("on:")) {
267
- if (val.kind === "expr") {
268
- attrs.push({ name: attrName.slice(3), value: { kind: "event", index: val.index } });
269
- }
1508
+ if (val.kind === "expr") attrs.push({ t: 3, name: attrName.slice(3), idx: val.idx });
1509
+ } else if (val.kind === "bool") {
1510
+ attrs.push({ t: 4, name: attrName });
1511
+ } else if (val.kind === "static") {
1512
+ attrs.push({ t: 0, name: attrName, value: val.value });
1513
+ } else if (val.kind === "expr") {
1514
+ attrs.push({ t: 1, name: attrName, idx: val.idx });
270
1515
  } else {
271
- attrs.push({ name: attrName, value: val });
1516
+ attrs.push({ t: 2, name: attrName, statics: val.statics, exprs: val.exprs });
272
1517
  }
273
1518
  }
274
1519
  return attrs;
@@ -282,40 +1527,67 @@ function parseTemplateToAST(strings) {
282
1527
  const idx = tryExprIdx();
283
1528
  if (idx >= 0) {
284
1529
  const collapsed2 = collapseWs(text2);
285
- if (collapsed2) children.push({ kind: "text", value: collapsed2 });
1530
+ if (collapsed2) children.push({ t: 1, value: collapsed2 });
286
1531
  text2 = "";
287
- children.push({ kind: "expr", index: idx });
1532
+ children.push({ t: 2, idx });
288
1533
  } else {
289
1534
  text2 += template2[pos++];
290
1535
  }
291
1536
  }
292
1537
  const collapsed = collapseWs(text2);
293
- if (collapsed) children.push({ kind: "text", value: collapsed });
1538
+ if (collapsed) children.push({ t: 1, value: collapsed });
294
1539
  }
295
1540
  function parseChildren() {
296
1541
  const children = [];
297
1542
  while (pos < len) {
298
1543
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") break;
299
1544
  if (template2[pos] === "<") {
1545
+ const next = template2[pos + 1];
1546
+ if (next === "!") {
1547
+ if (template2.startsWith("<!--", pos)) {
1548
+ const end = template2.indexOf("-->", pos + 4);
1549
+ pos = end === -1 ? len : end + 3;
1550
+ } else if (template2.startsWith("<![CDATA[", pos)) {
1551
+ const end = template2.indexOf("]]>", pos + 9);
1552
+ pos = end === -1 ? len : end + 3;
1553
+ } else {
1554
+ const end = template2.indexOf(">", pos);
1555
+ pos = end === -1 ? len : end + 1;
1556
+ }
1557
+ continue;
1558
+ }
1559
+ if (next === "?") {
1560
+ const end = template2.indexOf(">", pos);
1561
+ pos = end === -1 ? len : end + 1;
1562
+ continue;
1563
+ }
1564
+ if (!(next >= "a" && next <= "z") && !(next >= "A" && next <= "Z")) {
1565
+ children.push({ t: 1, value: "<" });
1566
+ pos++;
1567
+ continue;
1568
+ }
300
1569
  pos++;
301
1570
  const tag = readTagName();
302
1571
  const attrs = parseAttrs();
303
1572
  skipWs();
304
- const isVoid = VOID_ELEMENTS.has(tag);
1573
+ const isVoid = VOID_ELEMENTS2.has(tag);
305
1574
  const isSelfClosing = template2[pos] === "/";
306
1575
  if (isSelfClosing) pos++;
307
1576
  if (pos < len) pos++;
308
1577
  if (isVoid || isSelfClosing) {
309
- children.push({ kind: "element", el: { tag, attrs, children: [] } });
1578
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: [] } });
310
1579
  } else {
311
1580
  const inner = parseChildren();
1581
+ if (RAW_TEXT_TAGS.has(tag.toLowerCase()) && inner.some((c) => c.t === 2)) {
1582
+ throw new RuntimeParseError(tag);
1583
+ }
312
1584
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") {
313
1585
  pos += 2;
314
1586
  readTagName();
315
1587
  skipWs();
316
1588
  if (pos < len && template2[pos] === ">") pos++;
317
1589
  }
318
- children.push({ kind: "element", el: { tag, attrs, children: inner } });
1590
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: inner } });
319
1591
  }
320
1592
  } else {
321
1593
  parseTextChildren(children);
@@ -325,303 +1597,270 @@ function parseTemplateToAST(strings) {
325
1597
  }
326
1598
  return parseChildren();
327
1599
  }
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;
1600
+ var IDL_SYNCED_ATTRS = /* @__PURE__ */ new Set(["value", "checked"]);
1601
+ function generateBody(roots, h) {
1602
+ const lines = [];
1603
+ let n = 0;
1604
+ const q2 = jsStringLiteral;
1605
+ const P = h.prefix;
1606
+ function genElement(el) {
1607
+ const tag = el.tag.toLowerCase();
1608
+ if (tag === "meta" || tag === "link") return null;
1609
+ for (const attr of el.attrs) {
1610
+ if ((attr.t === 0 || attr.t === 4) && attr.name.toLowerCase() === "srcdoc") return null;
1611
+ }
1612
+ const v = `e${n++}`;
1613
+ h.tags.add(el.tag);
1614
+ if (el.svg) h.svg = true;
1615
+ lines.push(
1616
+ el.svg ? `const ${v} = document.createElementNS(${q2(SVG_NS)}, ${q2(el.tag)});` : `const ${v} = document.createElement(${q2(el.tag)});`
1617
+ );
1618
+ for (const attr of el.attrs) {
1619
+ switch (attr.t) {
1620
+ case 0:
1621
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, ${q2(attr.value)});`);
1622
+ break;
1623
+ case 4:
1624
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, "");`);
1625
+ break;
1626
+ case 1:
1627
+ case 2: {
1628
+ if (IDL_SYNCED_ATTRS.has(attr.name.toLowerCase())) return null;
1629
+ h.used.add("attr");
1630
+ if (attr.t === 1) {
1631
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, v${attr.idx});`);
1632
+ } else {
1633
+ h.used.add("str");
1634
+ let expr = q2(attr.statics[0]);
1635
+ for (let j = 0; j < attr.exprs.length; j++) {
1636
+ expr += ` + ${P}str(v${attr.exprs[j]}) + ${q2(attr.statics[j + 1])}`;
1637
+ }
1638
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, ${expr});`);
1639
+ }
1640
+ break;
1641
+ }
1642
+ case 3:
1643
+ h.used.add("on");
1644
+ lines.push(`${P}on(${v}, ${q2(attr.name)}, v${attr.idx});`);
1645
+ break;
1646
+ }
1647
+ }
1648
+ for (const child of el.children) {
1649
+ if (child.t === 0) {
1650
+ const c = genElement(child.el);
1651
+ if (c === null) return null;
1652
+ lines.push(`${v}.appendChild(${c});`);
1653
+ } else if (child.t === 1) {
1654
+ lines.push(`${v}.appendChild(document.createTextNode(${q2(child.value)}));`);
1655
+ } else {
1656
+ h.used.add("child");
1657
+ lines.push(`${P}child(${v}, v${child.idx});`);
1658
+ }
1659
+ }
1660
+ return v;
1661
+ }
1662
+ if (roots.length === 1 && roots[0].t === 0) {
1663
+ const root = genElement(roots[0].el);
1664
+ if (root === null) return null;
1665
+ lines.push(`return ${root};`);
1666
+ return lines.join("\n ");
1667
+ }
1668
+ lines.push(`const w = document.createElement("div");`);
1669
+ for (const root of roots) {
1670
+ if (root.t === 0) {
1671
+ const c = genElement(root.el);
1672
+ if (c === null) return null;
1673
+ lines.push(`w.appendChild(${c});`);
1674
+ } else if (root.t === 1) {
1675
+ lines.push(`w.appendChild(document.createTextNode(${q2(root.value)}));`);
1676
+ } else {
1677
+ return null;
351
1678
  }
352
1679
  }
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(", ")} }`);
1680
+ lines.push("return w;");
1681
+ return lines.join("\n ");
1682
+ }
1683
+ function helperSource(h) {
1684
+ const P = h.prefix;
1685
+ const out = [];
1686
+ if (h.used.has("attr")) {
1687
+ out.push(
1688
+ `function ${P}attr(el, name, value) {
1689
+ const teardown = ${P}bindAttrs(el, { [name]: value });
1690
+ if (typeof value === "function") ${P}registerDisposer(el, teardown);
1691
+ }`
1692
+ );
360
1693
  }
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]}`);
365
- } else {
366
- parts.push(`nodes: [${childExprs.join(", ")}]`);
367
- }
1694
+ if (h.used.has("str")) {
1695
+ out.push(`function ${P}str(value) {
1696
+ return value == null ? "" : String(value);
1697
+ }`);
368
1698
  }
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)({})`;
1699
+ if (h.used.has("on")) {
1700
+ out.push(
1701
+ `function ${P}on(el, name, handler) {
1702
+ if (typeof handler === "function") el.addEventListener(name, handler);
1703
+ else if (typeof __SIBU_DEV__ !== "undefined" && __SIBU_DEV__) {
1704
+ console.warn(\`[SibuJS] html: on:\${name} handler is not a function (got \${typeof handler}). Event listener was not attached.\`);
372
1705
  }
373
- return propsStr ? `${tag}(${propsStr})` : `${tag}({})`;
374
- }
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}]`;
1706
+ }`
1707
+ );
383
1708
  }
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);
1709
+ if (h.used.has("child")) {
1710
+ out.push(
1711
+ `function ${P}child(el, value) {
1712
+ if (typeof value === "function") {
1713
+ const frag = ${P}Fragment([value]);
1714
+ const ph = frag.firstChild;
1715
+ el.appendChild(frag);
1716
+ ${P}registerDisposer(el, () => ${P}dispose(ph));
1717
+ } else if (value instanceof Node) {
1718
+ el.appendChild(value);
1719
+ } else if (Array.isArray(value)) {
1720
+ for (let i = 0; i < value.length; i++) {
1721
+ const item = value[i];
1722
+ if (item instanceof Node) el.appendChild(item);
1723
+ else if (item != null && typeof item !== "boolean") el.appendChild(document.createTextNode(String(item)));
1724
+ }
1725
+ } else if (value != null && typeof value !== "boolean") {
1726
+ el.appendChild(document.createTextNode(String(value)));
1727
+ }
1728
+ }`
1729
+ );
411
1730
  }
412
- return { code: result, usedTags, usesSvg, compiledCount: templates.length };
1731
+ return out.join("\n");
413
1732
  }
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
- }
420
- }
1733
+ function importSource(h) {
1734
+ const P = h.prefix;
1735
+ const core = [];
1736
+ if (h.used.has("child")) core.push(`Fragment as ${P}Fragment`, `dispose as ${P}dispose`);
1737
+ if (h.used.has("attr") || h.used.has("child")) core.push(`registerDisposer as ${P}registerDisposer`);
1738
+ if (h.used.has("run")) core.push(`__renderParsedTemplate as ${P}run`);
1739
+ const lines = [];
1740
+ if (core.length > 0) lines.push(`import { ${core.join(", ")} } from "sibujs";`);
1741
+ if (h.used.has("attr")) lines.push(`import { bindAttrs as ${P}bindAttrs } from "sibujs/ui";`);
1742
+ return lines.join("\n");
421
1743
  }
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;
1744
+ function collectTags(children, h) {
1745
+ for (const child of children) {
1746
+ if (child.t !== 0) continue;
1747
+ h.tags.add(child.el.tag);
1748
+ if (child.el.svg) h.svg = true;
1749
+ collectTags(child.el.children, h);
1750
+ }
1751
+ }
1752
+ function planHtmlTemplates(code2) {
1753
+ const plan = {
1754
+ edits: [],
1755
+ append: "",
1756
+ usedTags: /* @__PURE__ */ new Set(),
1757
+ usesSvg: false,
1758
+ compiledCount: 0,
1759
+ skippedCount: 0
1760
+ };
1761
+ if (!code2.includes("`") || !code2.includes("sibujs")) return plan;
1762
+ const scan = scanModule(code2);
1763
+ if (!scan) return plan;
1764
+ const tagNames = /* @__PURE__ */ new Set();
1765
+ for (const decl of scan.imports) {
1766
+ if (!isSibuSource(decl.source)) continue;
1767
+ for (const b2 of decl.bindings) if (b2.imported === "html") tagNames.add(b2.local);
1768
+ }
1769
+ if (tagNames.size === 0) return plan;
1770
+ const uses = bindingUses(code2, scan, tagNames);
1771
+ if (uses.shadowed.size > 0 || uses.calls.length > 0 || uses.tagged.length === 0) return plan;
1772
+ const sites = new Set(uses.tagged.map((ref2) => ref2.tok));
1773
+ const h = { prefix: uniquePrefix(code2), used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1774
+ const functions = [];
1775
+ const compileTemplate = (tmpl) => {
1776
+ const strings = [];
1777
+ for (const qs of tmpl.quasis) {
1778
+ const cooked = cookEscapes(code2.slice(qs.start, qs.end));
1779
+ if (cooked === null) {
1780
+ if (tmpl.quasis.length === 1) return null;
1781
+ strings.push("undefined");
434
1782
  continue;
435
1783
  }
436
- pos++;
1784
+ strings.push(cooked);
437
1785
  }
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++;
1786
+ const name = `${h.prefix}t${functions.length}`;
1787
+ const params = tmpl.exprs.map((_, i2) => `v${i2}`).join(", ");
1788
+ let roots;
1789
+ try {
1790
+ roots = parseTemplate(strings);
1791
+ } catch (err) {
1792
+ if (err instanceof RuntimeParseError) {
1793
+ const message = `html: dynamic \${...} expressions are not allowed inside <${err.message}> (raw-text context). Build the content separately and append it as a Node.`;
1794
+ functions.push(`function ${name}(${params}) {
1795
+ throw new Error(${jsStringLiteral(message)});
1796
+ }`);
1797
+ return name;
1798
+ }
1799
+ throw err;
1800
+ }
1801
+ const scratch = { prefix: h.prefix, used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1802
+ const body2 = generateBody(roots, scratch);
1803
+ if (body2 === null) {
1804
+ const tree = JSON.stringify(roots).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1805
+ h.used.add("run");
1806
+ collectTags(roots, h);
1807
+ functions.push(
1808
+ `function ${name}(${params}) {
1809
+ return ${h.prefix}run(${name}.tree || (${name}.tree = ${tree}), [${params}]);
1810
+ }`
1811
+ );
1812
+ return name;
1813
+ }
1814
+ for (const u2 of scratch.used) h.used.add(u2);
1815
+ for (const t of scratch.tags) h.tags.add(t);
1816
+ if (scratch.svg) h.svg = true;
1817
+ functions.push(`function ${name}(${params}) {
1818
+ ${body2}
1819
+ }`);
1820
+ return name;
1821
+ };
1822
+ const replaceSite = (tag, tmpl, name) => {
1823
+ const edit = (start, end, before, after) => plan.edits.push({ start, end, text: `${before}${lineBreaksIn(code2, start, end)}${after}` });
1824
+ const exprs = tmpl.exprs;
1825
+ if (exprs.length === 0) {
1826
+ edit(tag.start, tmpl.end, `${name}(`, ")");
1827
+ return;
451
1828
  }
452
- exprs.push(code2.slice(exprStart, pos).trim());
453
- pos++;
454
- }
455
- return exprs;
456
- }
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 });
1829
+ edit(tag.start, exprs[0].start, `${name}((`, "");
1830
+ for (let i2 = 1; i2 < exprs.length; i2++) edit(exprs[i2 - 1].end, exprs[i2].start, ")", ", (");
1831
+ edit(exprs[exprs.length - 1].end, tmpl.end, "", "))");
1832
+ };
1833
+ const visit = (list) => {
1834
+ for (let i2 = 0; i2 < list.length; i2++) {
1835
+ const tok = list[i2];
1836
+ if (sites.has(tok)) {
1837
+ const tmpl = list[i2 + 1];
1838
+ for (const e of tmpl.exprs) visit(e.tokens);
1839
+ const name = compileTemplate(tmpl);
1840
+ if (name !== null) {
1841
+ plan.compiledCount++;
1842
+ replaceSite(tok, tmpl, name);
1843
+ } else {
1844
+ plan.skippedCount++;
1845
+ }
1846
+ i2++;
1847
+ } else if (tok.type === "template") {
1848
+ for (const e of tok.exprs) visit(e.tokens);
538
1849
  }
539
1850
  }
540
- }
541
- return {
542
- hasStaticPatterns: patterns.length > 0,
543
- patterns
544
1851
  };
545
- }
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;
557
- }
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))}"`);
586
- }
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;");
1852
+ visit(scan.tokens);
1853
+ if (plan.compiledCount === 0) return { ...plan, edits: [] };
1854
+ const trailer = [importSource(h), helperSource(h), ...functions].filter(Boolean).join("\n");
1855
+ plan.append = `
1856
+ ${trailer}
1857
+ `;
1858
+ plan.usedTags = h.tags;
1859
+ plan.usesSvg = h.svg;
1860
+ return plan;
599
1861
  }
600
1862
 
601
1863
  // 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
1864
  var DEFAULT_INCLUDE = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"];
626
1865
  var DEFAULT_EXCLUDE = ["node_modules/**", "dist/**", "**/*.test.*", "**/*.spec.*"];
627
1866
  function matchesPattern(filePath, patterns) {
@@ -631,30 +1870,42 @@ function matchesPattern(filePath, patterns) {
631
1870
  return new RegExp(regexStr).test(normalized);
632
1871
  });
633
1872
  }
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(");
1873
+ function devPrologueEdit(code2) {
1874
+ let at = 0;
1875
+ if (code2.startsWith("#!")) {
1876
+ const nl = code2.indexOf("\n");
1877
+ if (nl === -1) return null;
1878
+ at = nl + 1;
648
1879
  }
649
- return result;
1880
+ return {
1881
+ start: at,
1882
+ end: at,
1883
+ text: '/* SibuJS Dev Mode */ if (typeof globalThis !== "undefined") { globalThis.__SIBU_DEV__ = true; } '
1884
+ };
650
1885
  }
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;
1886
+ function staticOptimizationEdits(code2) {
1887
+ const analysis = analyzeStaticTemplates(code2);
1888
+ if (!analysis.hasStaticPatterns) return null;
1889
+ const P = uniquePrefix(code2);
1890
+ const edits = analysis.patterns.map((pattern2) => ({
1891
+ start: pattern2.start,
1892
+ end: pattern2.end,
1893
+ // A call spanning several lines keeps its line breaks.
1894
+ text: `${P}static(${jsStringLiteral(pattern2.templateHtml)}${lineBreaksIn(code2, pattern2.start, pattern2.end)})`
1895
+ }));
1896
+ const append = `
1897
+ import { staticTemplate as ${P}staticTemplate } from "sibujs/performance";
1898
+ function ${P}static(markup) {
1899
+ return document.adoptNode(${P}staticTemplate(markup));
1900
+ }
1901
+ `;
1902
+ return { edits, append };
1903
+ }
1904
+ function resolveDevMode(explicit, info) {
1905
+ if (explicit !== void 0) return explicit;
1906
+ if (info?.command === "build") return info.mode === "development";
1907
+ if (info?.command === "serve") return true;
1908
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
658
1909
  }
659
1910
  function sibuVitePlugin(options = {}) {
660
1911
  const {
@@ -663,22 +1914,23 @@ function sibuVitePlugin(options = {}) {
663
1914
  include = DEFAULT_INCLUDE,
664
1915
  exclude = DEFAULT_EXCLUDE,
665
1916
  devMode,
666
- staticOptimize,
1917
+ staticOptimize = false,
667
1918
  compileTemplates
668
1919
  } = options;
669
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
1920
+ let isDevMode = resolveDevMode(devMode, void 0);
670
1921
  return {
671
1922
  name: "sibu-vite-plugin",
672
1923
  enforce: "pre",
673
- config() {
1924
+ config(_userConfig, env) {
1925
+ isDevMode = resolveDevMode(devMode, env);
674
1926
  return {
675
- // Optimize dependency pre-bundling for sibu
1927
+ // Optimize dependency pre-bundling for sibujs
676
1928
  optimizeDeps: {
677
- include: ["sibu"]
1929
+ include: ["sibujs"]
678
1930
  },
679
- // Ensure sibu is treated correctly for SSR
1931
+ // Ensure sibujs is treated correctly for SSR
680
1932
  ssr: {
681
- noExternal: ["sibu"]
1933
+ noExternal: ["sibujs"]
682
1934
  },
683
1935
  // Define global constants for dead code elimination
684
1936
  define: {
@@ -691,69 +1943,47 @@ function sibuVitePlugin(options = {}) {
691
1943
  }
692
1944
  };
693
1945
  },
1946
+ configResolved(config) {
1947
+ isDevMode = resolveDevMode(devMode, config);
1948
+ },
694
1949
  transform(code2, id) {
695
1950
  if (!matchesPattern(id, include) || matchesPattern(id, exclude)) {
696
1951
  return null;
697
1952
  }
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
- }
1953
+ if (!code2.includes("sibujs")) return null;
1954
+ if (!scanModule(code2)) return null;
1955
+ const edits = [];
1956
+ let append = "";
1957
+ if (isDevMode && importsSibu(code2)) {
1958
+ const prologue = devPrologueEdit(code2);
1959
+ if (prologue) edits.push(prologue);
706
1960
  }
707
- if (isDevMode) {
708
- const withDevHelpers = injectDevHelpers(transformed);
709
- if (withDevHelpers !== transformed) {
710
- transformed = withDevHelpers;
711
- modified = true;
712
- }
1961
+ if (pureAnnotations) {
1962
+ edits.push(...pureAnnotationEdits(code2));
713
1963
  }
714
1964
  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;
1965
+ if (shouldCompile) {
1966
+ const plan = planHtmlTemplates(code2);
1967
+ if (plan.compiledCount > 0) {
1968
+ edits.push(...plan.edits);
1969
+ append += plan.append;
734
1970
  }
735
1971
  }
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;
1972
+ if (staticOptimize) {
1973
+ const optimized = staticOptimizationEdits(code2);
1974
+ if (optimized) {
1975
+ edits.push(...optimized.edits);
1976
+ append += optimized.append;
750
1977
  }
751
1978
  }
752
- if (!modified) return null;
1979
+ if (edits.length === 0 && append === "") return null;
1980
+ const sorted = normalizeEdits(edits);
1981
+ if (!sorted) return null;
1982
+ const transformed = applyEdits(code2, sorted, append);
1983
+ if (transformed === code2) return null;
753
1984
  return {
754
1985
  code: transformed,
755
- map: void 0
756
- // Let Vite handle source maps
1986
+ map: editsSourceMap(code2, sorted, append, id)
757
1987
  };
758
1988
  },
759
1989
  handleHotUpdate(ctx) {
@@ -812,7 +2042,7 @@ function createViteConfig(options = {}) {
812
2042
  // SSR configuration
813
2043
  ...ssr ? {
814
2044
  ssr: {
815
- noExternal: ["sibu"],
2045
+ noExternal: ["sibujs"],
816
2046
  target: "node"
817
2047
  },
818
2048
  build: {
@@ -829,8 +2059,8 @@ function createViteConfig(options = {}) {
829
2059
  } : {},
830
2060
  // Optimize dependency handling
831
2061
  optimizeDeps: {
832
- include: ["sibu"],
833
- // Force pre-bundling of sibu for faster dev startup
2062
+ include: ["sibujs"],
2063
+ // Force pre-bundling of sibujs for faster dev startup
834
2064
  force: false
835
2065
  },
836
2066
  // Environment variable handling
@@ -852,30 +2082,20 @@ function deepMerge(target, source2) {
852
2082
  return result;
853
2083
  }
854
2084
 
855
- // src/build/webpack.ts
856
- var PURE_FACTORIES = [
857
- "tagFactory",
858
- "context",
859
- "defineComponent",
860
- "withProps",
861
- "withDefaults",
862
- "pure",
863
- "noSideEffect"
864
- ];
2085
+ // src/build/webpack.ts
865
2086
  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;
2087
+ return injectPureAnnotations(source2);
872
2088
  }
873
2089
  function sibuWebpackPlugin(options = {}) {
874
- const { pureAnnotations = true, devMode } = options;
875
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
2090
+ const { devMode } = options;
876
2091
  return {
877
2092
  name: "SibuWebpackPlugin",
878
2093
  apply(compiler) {
2094
+ if (options.pureAnnotations === true) {
2095
+ console.warn(
2096
+ '[SibuWebpackPlugin] `pureAnnotations` is not applied by the plugin: webpack needs a loader file. Create sibu-pure-loader.cjs containing `module.exports = require("sibujs/build").createPureAnnotationsLoader();` and add a module rule `{ test: /\\.[cm]?[jt]sx?$/, exclude: /node_modules/, use: "./sibu-pure-loader.cjs" }`.'
2097
+ );
2098
+ }
879
2099
  compiler.hooks?.compilation?.tap("SibuWebpackPlugin", (compilation) => {
880
2100
  const comp = compilation;
881
2101
  if (comp.hooks?.optimizeModules) {
@@ -883,31 +2103,6 @@ function sibuWebpackPlugin(options = {}) {
883
2103
  });
884
2104
  }
885
2105
  });
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
2106
  compiler.hooks?.afterResolvers?.tap("SibuWebpackPlugin", () => {
912
2107
  if (!compiler.options.resolve) {
913
2108
  compiler.options.resolve = {};
@@ -919,20 +2114,21 @@ function sibuWebpackPlugin(options = {}) {
919
2114
  compiler.options.resolve.mainFields.unshift("module");
920
2115
  }
921
2116
  });
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
- }
2117
+ const tapDoneLogger = () => compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
2118
+ const statsObj = stats;
2119
+ const info = statsObj?.toJson?.({ modules: false, chunks: false });
2120
+ if (info) {
2121
+ console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
2122
+ const warnings = info.warnings;
2123
+ if (warnings?.length) {
2124
+ console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
932
2125
  }
933
- });
934
- }
2126
+ }
2127
+ });
2128
+ if (devMode === true) tapDoneLogger();
935
2129
  compiler.hooks?.environment?.tap("SibuWebpackPlugin", () => {
2130
+ const isDevMode = devMode ?? resolveWebpackDevMode(compiler.options);
2131
+ if (devMode === void 0 && isDevMode) tapDoneLogger();
936
2132
  if (!compiler.options.plugins) {
937
2133
  compiler.options.plugins = [];
938
2134
  }
@@ -940,10 +2136,20 @@ function sibuWebpackPlugin(options = {}) {
940
2136
  __SIBU_DEV__: JSON.stringify(isDevMode)
941
2137
  };
942
2138
  compiler.__sibuDefines = defines;
2139
+ const DefinePlugin = compiler.webpack?.DefinePlugin;
2140
+ if (DefinePlugin) {
2141
+ new DefinePlugin(defines).apply(compiler);
2142
+ }
943
2143
  });
944
2144
  }
945
2145
  };
946
2146
  }
2147
+ function resolveWebpackDevMode(options) {
2148
+ const mode = options.mode;
2149
+ if (mode === "development") return true;
2150
+ if (mode === "production" || mode === void 0) return false;
2151
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
2152
+ }
947
2153
  function createPureAnnotationsLoader() {
948
2154
  return function sibuPureAnnotationsLoader(source2) {
949
2155
  return addPureAnnotations(source2);
@@ -1012,9 +2218,10 @@ function createWebpackConfig(options = {}) {
1012
2218
  splitChunks: isDev2 ? false : {
1013
2219
  chunks: "all",
1014
2220
  cacheGroups: {
1015
- // Separate sibu framework code into its own chunk
2221
+ // Separate sibujs framework code into its own chunk (the package
2222
+ // directory is `sibujs`; matching `sibu` never selected anything)
1016
2223
  sibu: {
1017
- test: /[\\/]node_modules[\\/]sibu[\\/]/,
2224
+ test: /[\\/]node_modules[\\/]sibujs[\\/]/,
1018
2225
  name: "sibu",
1019
2226
  chunks: "all",
1020
2227
  priority: 20
@@ -1065,8 +2272,9 @@ __export(index_exports, {
1065
2272
  Loading: () => Loading,
1066
2273
  MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
1067
2274
  Portal: () => Portal,
1068
- SVG_NS: () => SVG_NS2,
2275
+ SVG_NS: () => SVG_NS4,
1069
2276
  Suspense: () => Suspense,
2277
+ __renderParsedTemplate: () => __renderParsedTemplate,
1070
2278
  __resetIdCounter: () => __resetIdCounter,
1071
2279
  a: () => a,
1072
2280
  abbr: () => abbr,
@@ -1381,33 +2589,6 @@ function reportError(error, context2) {
1381
2589
  });
1382
2590
  }
1383
2591
 
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
2592
  // src/core/rendering/dispose.ts
1412
2593
  var elementDisposers = /* @__PURE__ */ new WeakMap();
1413
2594
  var MAX_DRAIN_TEARDOWNS = 1e4;
@@ -1447,6 +2628,49 @@ function registerDisposer(node, teardown) {
1447
2628
  const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
1448
2629
  if (capture) capture.entries.push([node, teardown]);
1449
2630
  }
2631
+ function withDisposerRollback(build) {
2632
+ const frame = { entries: [], closed: false };
2633
+ const captured = frame.entries;
2634
+ registrationCaptures.push(frame);
2635
+ let result;
2636
+ try {
2637
+ result = build();
2638
+ } catch (err) {
2639
+ let executed = 0;
2640
+ try {
2641
+ while (captured.length > 0) {
2642
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2643
+ reportDrainRunaway("rollback", executed, captured.length);
2644
+ break;
2645
+ }
2646
+ const [node, teardown] = captured.pop();
2647
+ if (!unregisterDisposer(node, teardown)) continue;
2648
+ executed++;
2649
+ try {
2650
+ teardown();
2651
+ } catch (cleanupErr) {
2652
+ reportError(cleanupErr, { phase: "cleanup", name: "disposer" });
2653
+ }
2654
+ }
2655
+ } finally {
2656
+ frame.closed = true;
2657
+ captured.length = 0;
2658
+ registrationCaptures.pop();
2659
+ }
2660
+ throw err;
2661
+ }
2662
+ frame.closed = true;
2663
+ registrationCaptures.pop();
2664
+ const parent = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
2665
+ if (parent) {
2666
+ for (const entry of captured) {
2667
+ if (elementDisposers.get(entry[0])?.includes(entry[1])) parent.entries.push(entry);
2668
+ }
2669
+ frame.forwardTo = parent;
2670
+ }
2671
+ captured.length = 0;
2672
+ return result;
2673
+ }
1450
2674
  function unregisterDisposer(node, teardown) {
1451
2675
  const disposers = elementDisposers.get(node);
1452
2676
  if (!disposers) return false;
@@ -1466,296 +2690,283 @@ function dispose(node) {
1466
2690
  const children = Array.from(current.childNodes);
1467
2691
  for (let i2 = 0; i2 < children.length; i2++) {
1468
2692
  stack.push(children[i2]);
1469
- }
1470
- }
1471
- for (let i2 = order.length - 1; i2 >= 0; i2--) {
1472
- disposeNodeOwn(order[i2]);
1473
- }
1474
- }
1475
- function disposeNodeOwn(node) {
1476
- if (!elementDisposers.has(node)) return;
1477
- let executed = 0;
1478
- let runaway = false;
1479
- while (!runaway) {
1480
- const pending = elementDisposers.get(node);
1481
- if (!pending || pending.length === 0) break;
1482
- const snapshot = pending.slice();
1483
- elementDisposers.delete(node);
1484
- if (DEV) activeBindingCount -= snapshot.length;
1485
- for (let i2 = 0; i2 < snapshot.length; i2++) {
1486
- if (executed >= MAX_DRAIN_TEARDOWNS) {
1487
- const rest = snapshot.slice(i2);
1488
- const added = elementDisposers.get(node);
1489
- elementDisposers.set(node, added ? rest.concat(added) : rest);
1490
- if (DEV) activeBindingCount += rest.length;
1491
- reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1492
- runaway = true;
1493
- break;
1494
- }
1495
- executed++;
1496
- try {
1497
- snapshot[i2]();
1498
- } catch (err) {
1499
- reportError(err, { phase: "cleanup", name: "disposer" });
1500
- }
1501
- }
1502
- }
1503
- }
1504
- function replaceChildrenSafely(parent, ...next) {
1505
- for (let i2 = 0; i2 < next.length; i2++) {
1506
- const node = next[i2];
1507
- node.parentNode?.removeChild(node);
1508
- }
1509
- const current = Array.from(parent.childNodes);
1510
- for (let i2 = 0; i2 < current.length; i2++) {
1511
- dispose(current[i2]);
1512
- }
1513
- parent.replaceChildren(...next);
1514
- }
1515
- function checkLeaks(warnThreshold = 0) {
1516
- if (!DEV) return 0;
1517
- if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
1518
- devWarn(
1519
- `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
1520
- );
1521
- }
1522
- return activeBindingCount;
1523
- }
1524
-
1525
- // src/utils/sanitize.ts
1526
- function stripControlChars(value) {
1527
- return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
1528
- }
1529
- function isEventHandlerAttr(name) {
1530
- if (name.length < 3) return false;
1531
- const lower = name.toLowerCase();
1532
- return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
1533
- }
1534
- var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1535
- var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
1536
- function sanitizeUrl(url) {
1537
- const probe = stripControlChars(url).trim();
1538
- if (!probe) return "";
1539
- const output2 = url.replace(OUTER_TRIM, "");
1540
- const lower = probe.toLowerCase();
1541
- let schemeEnd = -1;
1542
- for (let i2 = 0; i2 < lower.length; i2++) {
1543
- const ch = lower.charCodeAt(i2);
1544
- if (ch === 58) {
1545
- schemeEnd = i2;
1546
- break;
1547
- }
1548
- if (ch === 47 || ch === 63 || ch === 35) break;
1549
- }
1550
- if (schemeEnd === -1) return output2;
1551
- const scheme = lower.slice(0, schemeEnd + 1);
1552
- if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
1553
- if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1554
- return output2;
1555
- }
1556
- var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
1557
- function sanitizeSrcset(value) {
1558
- const parts = value.split(",");
1559
- const out = [];
1560
- for (let i2 = 0; i2 < parts.length; i2++) {
1561
- const part = parts[i2].trim();
1562
- if (!part) continue;
1563
- const m = part.match(/^(\S+)(\s+.+)?$/);
1564
- if (!m) continue;
1565
- if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
1566
- const safe = sanitizeUrl(m[1]);
1567
- if (!safe) continue;
1568
- out.push(m[2] ? `${safe}${m[2]}` : safe);
1569
- }
1570
- return out.join(", ");
1571
- }
1572
- var CSS_DANGER_GATE = /[(:@\\]/;
1573
- function isCssWhitespace(ch) {
1574
- return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
1575
- }
1576
- function isHexDigit(ch) {
1577
- return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
1578
- }
1579
- function decodeCssEscapes(value) {
1580
- let out = "";
1581
- for (let i2 = 0; i2 < value.length; i2++) {
1582
- const ch = value[i2];
1583
- if (ch !== "\\") {
1584
- out += ch;
1585
- continue;
1586
- }
1587
- if (i2 + 1 >= value.length) break;
1588
- const next = value[i2 + 1];
1589
- if (next === "\n" || next === "\f") {
1590
- i2 += 1;
1591
- continue;
1592
- }
1593
- if (next === "\r") {
1594
- i2 += value[i2 + 2] === "\n" ? 2 : 1;
1595
- continue;
1596
- }
1597
- if (isHexDigit(next)) {
1598
- let hex = "";
1599
- let j = i2 + 1;
1600
- while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
1601
- hex += value[j];
1602
- j++;
1603
- }
1604
- if (j < value.length && isCssWhitespace(value[j])) {
1605
- j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
1606
- }
1607
- const code2 = Number.parseInt(hex, 16);
1608
- const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
1609
- out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
1610
- i2 = j - 1;
1611
- continue;
1612
- }
1613
- out += next;
1614
- i2 += 1;
1615
- }
1616
- return out;
1617
- }
1618
- var BLOCKED_CSS_NEEDLES = [
1619
- "url(",
1620
- "expression(",
1621
- "javascript:",
1622
- "vbscript:",
1623
- "-moz-binding",
1624
- "behavior:",
1625
- "@import",
1626
- "image-set(",
1627
- "filter:progid"
1628
- ];
1629
- function describeElement(el) {
1630
- if (!el) return "an element";
1631
- let out = el.tagName.toLowerCase();
1632
- const id = el.getAttribute("id");
1633
- if (id) out += `#${id}`;
1634
- const cls = el.getAttribute("class");
1635
- if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
1636
- return `<${out}>`;
1637
- }
1638
- var warnedDeclarations = /* @__PURE__ */ new Set();
1639
- var MAX_WARNED_DECLARATIONS = 100;
1640
- var declarationCapAnnounced = false;
1641
- function warnDroppedDeclaration(value, reason, ctx) {
1642
- devWarnLazy(() => {
1643
- const reasons = [
1644
- "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",
1645
- "expression() executes arbitrary script in legacy engines",
1646
- "the javascript: scheme executes script",
1647
- "the vbscript: scheme executes script",
1648
- "-moz-binding loads and runs remote XBL",
1649
- "behavior: loads and runs a remote HTC script",
1650
- "@import pulls in a remote stylesheet",
1651
- "image-set() fetches remote images, the same exfiltration channel as url()",
1652
- "filter:progid activates legacy scriptable filters"
1653
- ];
1654
- const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
1655
- const where = describeElement(ctx?.element);
1656
- const key = `${where}|${declaration}`;
1657
- if (warnedDeclarations.has(key)) return "";
1658
- if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
1659
- if (declarationCapAnnounced) return "";
1660
- declarationCapAnnounced = true;
1661
- 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.`;
1662
- }
1663
- warnedDeclarations.add(key);
1664
- 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.`;
1665
- });
1666
- }
1667
- function blockedCssReason(value) {
1668
- if (!CSS_DANGER_GATE.test(value)) return -1;
1669
- const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
1670
- const lower = normalized.toLowerCase().replace(/\s+/g, "");
1671
- for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
1672
- if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
2693
+ }
2694
+ }
2695
+ for (let i2 = order.length - 1; i2 >= 0; i2--) {
2696
+ disposeNodeOwn(order[i2]);
1673
2697
  }
1674
- return -1;
1675
2698
  }
1676
- function sanitizeCSSValue(value, context2) {
1677
- const reason = blockedCssReason(value);
1678
- if (reason === -1) return value;
1679
- warnDroppedDeclaration(value, reason, context2);
1680
- return "";
2699
+ function disposeNodeOwn(node) {
2700
+ if (!elementDisposers.has(node)) return;
2701
+ let executed = 0;
2702
+ let runaway = false;
2703
+ while (!runaway) {
2704
+ const pending = elementDisposers.get(node);
2705
+ if (!pending || pending.length === 0) break;
2706
+ const snapshot = pending.slice();
2707
+ elementDisposers.delete(node);
2708
+ if (DEV) activeBindingCount -= snapshot.length;
2709
+ for (let i2 = 0; i2 < snapshot.length; i2++) {
2710
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2711
+ const rest = snapshot.slice(i2);
2712
+ const added = elementDisposers.get(node);
2713
+ elementDisposers.set(node, added ? rest.concat(added) : rest);
2714
+ if (DEV) activeBindingCount += rest.length;
2715
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
2716
+ runaway = true;
2717
+ break;
2718
+ }
2719
+ executed++;
2720
+ try {
2721
+ snapshot[i2]();
2722
+ } catch (err) {
2723
+ reportError(err, { phase: "cleanup", name: "disposer" });
2724
+ }
2725
+ }
2726
+ }
1681
2727
  }
1682
- function sanitizeStyleAttribute(cssText, context2) {
1683
- const input2 = String(cssText);
1684
- if (input2.trim() === "") return "";
1685
- if (typeof document === "undefined") {
1686
- return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
2728
+ function replaceChildrenSafely(parent, ...next) {
2729
+ for (let i2 = 0; i2 < next.length; i2++) {
2730
+ const node = next[i2];
2731
+ node.parentNode?.removeChild(node);
1687
2732
  }
1688
- const probe = document.createElement("div");
1689
- try {
1690
- probe.style.cssText = input2;
1691
- } catch {
1692
- return "";
2733
+ const current = Array.from(parent.childNodes);
2734
+ for (let i2 = 0; i2 < current.length; i2++) {
2735
+ dispose(current[i2]);
1693
2736
  }
1694
- const declarations = [];
1695
- for (let i2 = 0; i2 < probe.style.length; i2++) {
1696
- const property = probe.style[i2];
1697
- const value = probe.style.getPropertyValue(property);
1698
- let reason = blockedCssReason(value);
1699
- if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
1700
- if (reason !== -1) {
1701
- warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1702
- continue;
1703
- }
1704
- const priority = probe.style.getPropertyPriority(property);
1705
- declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
2737
+ parent.replaceChildren(...next);
2738
+ }
2739
+ function checkLeaks(warnThreshold = 0) {
2740
+ if (!DEV) return 0;
2741
+ if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
2742
+ devWarn(
2743
+ `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
2744
+ );
1706
2745
  }
1707
- return declarations.join("; ");
2746
+ return activeBindingCount;
1708
2747
  }
1709
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1710
- "href",
1711
- "xlink:href",
1712
- "src",
1713
- "action",
1714
- "formaction",
1715
- "formtarget",
1716
- "cite",
1717
- "poster",
1718
- "background",
1719
- "srcset",
1720
- "ping",
1721
- "data",
1722
- // `<html manifest>` is a URL sink too. It was carried only by the SSR
1723
- // serializers' private copy of this set; folding that copy into this one would
1724
- // otherwise have silently dropped its coverage.
1725
- "manifest"
1726
- ]);
1727
- var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
1728
- function isHtmlContentAttribute(name) {
1729
- return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
2748
+
2749
+ // src/utils/metaRefresh.ts
2750
+ function isAsciiWhitespace(ch) {
2751
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
1730
2752
  }
1731
- function sanitizeAttributeString(attr, value, context2) {
1732
- const lower = attr.toLowerCase();
1733
- if (lower === "srcset") return sanitizeSrcset(value);
1734
- if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1735
- if (lower === "style") return sanitizeStyleAttribute(value, context2);
1736
- return value;
2753
+ function trimAsciiWhitespace(value) {
2754
+ let start = 0;
2755
+ let end = value.length;
2756
+ while (start < end && isAsciiWhitespace(value[start])) start++;
2757
+ while (end > start && isAsciiWhitespace(value[end - 1])) end--;
2758
+ return value.slice(start, end);
2759
+ }
2760
+ var forbidden = (reason) => ({ kind: "forbidden", reason });
2761
+ function parseMetaRefreshContent(content) {
2762
+ const raw = trimAsciiWhitespace(stripControlChars(content));
2763
+ if (raw === "") return forbidden(DEV ? "empty content" : "");
2764
+ let i2 = 0;
2765
+ while (i2 < raw.length && raw[i2] >= "0" && raw[i2] <= "9") i2++;
2766
+ if (i2 === 0) return forbidden(DEV ? "missing or non-numeric delay" : "");
2767
+ const delay = Number.parseInt(raw.slice(0, i2), 10);
2768
+ if (!Number.isFinite(delay)) return forbidden(DEV ? "unparseable delay" : "");
2769
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
2770
+ if (i2 === raw.length) return { kind: "delay-only", delay };
2771
+ if (raw[i2] !== ";" && raw[i2] !== ",") return forbidden(DEV ? "unexpected text after delay" : "");
2772
+ i2++;
2773
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
2774
+ const keyStart = i2;
2775
+ while (i2 < raw.length && !isAsciiWhitespace(raw[i2]) && raw[i2] !== "=") i2++;
2776
+ const key = raw.slice(keyStart, i2).toLowerCase();
2777
+ if (key !== "url") return forbidden(DEV ? `unsupported refresh key ${JSON.stringify(key)}` : "");
2778
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
2779
+ if (raw[i2] !== "=") return forbidden(DEV ? "missing '=' after url" : "");
2780
+ i2++;
2781
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
2782
+ let destination;
2783
+ const quote2 = raw[i2];
2784
+ if (quote2 === '"' || quote2 === "'") {
2785
+ const close = raw.indexOf(quote2, i2 + 1);
2786
+ if (close === -1) return forbidden(DEV ? "unterminated quoted destination" : "");
2787
+ destination = raw.slice(i2 + 1, close);
2788
+ const rest = trimAsciiWhitespace(raw.slice(close + 1));
2789
+ if (rest !== "") return forbidden(DEV ? "trailing text after quoted destination" : "");
2790
+ } else {
2791
+ destination = trimAsciiWhitespace(raw.slice(i2));
2792
+ if (/[;,]/.test(destination)) return forbidden(DEV ? "multiple refresh assignments" : "");
2793
+ }
2794
+ if (destination === "") return forbidden(DEV ? "empty destination" : "");
2795
+ const sanitizedDestination = sanitizeUrl(destination);
2796
+ if (sanitizedDestination === "") return forbidden(DEV ? "destination uses a disallowed protocol" : "");
2797
+ return { kind: "allowed", delay, destination, sanitizedDestination };
2798
+ }
2799
+ function resolveMetaRefreshPolicy(attributes) {
2800
+ let httpEquiv;
2801
+ let content;
2802
+ let duplicateName;
2803
+ const seen = /* @__PURE__ */ new Set();
2804
+ for (const [key, value] of attributes) {
2805
+ const canonical = canonicalAttrName(key);
2806
+ if (seen.has(canonical)) duplicateName = canonical;
2807
+ seen.add(canonical);
2808
+ if (canonical === "http-equiv") httpEquiv = value;
2809
+ else if (canonical === "content") content = value;
2810
+ }
2811
+ if (duplicateName !== void 0) {
2812
+ return forbidden(DEV ? `duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}` : "");
2813
+ }
2814
+ if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
2815
+ if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
2816
+ return { kind: "not-refresh" };
2817
+ }
2818
+ if (typeof content !== "string") return { kind: "not-refresh" };
2819
+ return parseMetaRefreshContent(content);
1737
2820
  }
1738
2821
 
1739
- // src/utils/setSafeAttribute.ts
2822
+ // src/utils/elementPolicy.ts
1740
2823
  var HTML_NS = "http://www.w3.org/1999/xhtml";
1741
- var SVG_NS = "http://www.w3.org/2000/svg";
2824
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
2825
+ var BLOCKED_ELEMENTS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
2826
+ function canonicalTagName(tag) {
2827
+ return canonicalAttrName(tag);
2828
+ }
2829
+ function isBlockedElement(tag) {
2830
+ return BLOCKED_ELEMENTS.has(canonicalTagName(tag));
2831
+ }
2832
+ var CODE_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style"]);
2833
+ function isCodeTextElement(tag) {
2834
+ return CODE_TEXT_ELEMENTS.has(canonicalTagName(tag));
2835
+ }
2836
+ var ContextualRefusal = {
2837
+ None: 0,
2838
+ /** The element would carry a refresh directive the shared policy forbids. */
2839
+ MetaRefreshForbidden: 1,
2840
+ /** A `<meta>` with a reactive binding may not carry any refresh directive. */
2841
+ MetaRefreshReactive: 2,
2842
+ /** A runtime value may not choose which program a `<script>` runs. */
2843
+ ScriptSource: 3,
2844
+ /** An SVG animation may not target a URL, handler or document attribute. */
2845
+ AnimationTarget: 4,
2846
+ /** A runtime-written `href` may not become an applied stylesheet. */
2847
+ StylesheetSource: 5
2848
+ };
2849
+ var SCRIPT_SOURCE_ATTRIBUTES = /* @__PURE__ */ new Set(["src", "href", "xlink:href", "type", "language"]);
2850
+ var SVG_ANIMATION_ELEMENTS = /* @__PURE__ */ new Set(["animate", "set", "animateMotion", "animateTransform", "animateColor"]);
2851
+ var reactiveMetaElements = /* @__PURE__ */ new WeakSet();
2852
+ function isHtmlNamespace(el) {
2853
+ return el.namespaceURI === HTML_NS || el.namespaceURI === null;
2854
+ }
2855
+ function metaSnapshot(el, canonical, value) {
2856
+ const snapshot = /* @__PURE__ */ new Map();
2857
+ const attrs = el.attributes;
2858
+ for (let i2 = 0; i2 < attrs.length; i2++) {
2859
+ const attr = attrs[i2];
2860
+ if (attr.name === canonical) continue;
2861
+ snapshot.set(attr.name, attr.value);
2862
+ }
2863
+ if (canonical !== null && value !== null) snapshot.set(canonical, value);
2864
+ return snapshot;
2865
+ }
2866
+ function claimReactiveMeta(el) {
2867
+ if (reactiveMetaElements.has(el)) return;
2868
+ reactiveMetaElements.add(el);
2869
+ if (el.getAttribute("content") === null) return;
2870
+ if (resolveMetaRefreshPolicy(metaSnapshot(el, null, null)).kind !== "not-refresh") {
2871
+ el.removeAttribute("content");
2872
+ }
2873
+ }
2874
+ function metaRefusal(el, name, value, origin) {
2875
+ if (!isHtmlNamespace(el)) return ContextualRefusal.None;
2876
+ const canonical = canonicalAttrName(name);
2877
+ if (canonical !== "http-equiv" && canonical !== "content") return ContextualRefusal.None;
2878
+ if (origin === "reactive") claimReactiveMeta(el);
2879
+ if (value === null) return ContextualRefusal.None;
2880
+ if (canonical === "content" && !el.hasAttribute("http-equiv")) return ContextualRefusal.None;
2881
+ const decision = resolveMetaRefreshPolicy(metaSnapshot(el, canonical, value));
2882
+ if (decision.kind === "forbidden") return ContextualRefusal.MetaRefreshForbidden;
2883
+ if (decision.kind !== "not-refresh" && reactiveMetaElements.has(el)) return ContextualRefusal.MetaRefreshReactive;
2884
+ return ContextualRefusal.None;
2885
+ }
2886
+ function animationRefusal(name, value) {
2887
+ if (value === null || canonicalAttrName(name) !== "attributename") return ContextualRefusal.None;
2888
+ const target = stripControlChars(value);
2889
+ return isUrlAttribute(target) || isEventHandlerAttr(target) || isHtmlContentAttribute(target) ? ContextualRefusal.AnimationTarget : ContextualRefusal.None;
2890
+ }
2891
+ var runtimeHrefLinks = /* @__PURE__ */ new WeakSet();
2892
+ function relHasStylesheet(rel) {
2893
+ if (rel === null) return false;
2894
+ const lower = canonicalAttrName(rel);
2895
+ if (!lower.includes("stylesheet")) return false;
2896
+ const tokens = lower.split(/[\t\n\f\r ]+/);
2897
+ for (let i2 = 0; i2 < tokens.length; i2++) if (tokens[i2] === "stylesheet") return true;
2898
+ return false;
2899
+ }
2900
+ function stylesheetRefusal(el, name, value, origin) {
2901
+ if (!isHtmlNamespace(el)) return ContextualRefusal.None;
2902
+ const canonical = canonicalAttrName(name);
2903
+ if (canonical === "href") {
2904
+ if (origin === "static" || value === null) return ContextualRefusal.None;
2905
+ if (relHasStylesheet(el.getAttribute("rel"))) return ContextualRefusal.StylesheetSource;
2906
+ runtimeHrefLinks.add(el);
2907
+ return ContextualRefusal.None;
2908
+ }
2909
+ if (canonical === "rel") {
2910
+ if (!relHasStylesheet(value)) return ContextualRefusal.None;
2911
+ return runtimeHrefLinks.has(el) && el.hasAttribute("href") ? ContextualRefusal.StylesheetSource : ContextualRefusal.None;
2912
+ }
2913
+ return ContextualRefusal.None;
2914
+ }
2915
+ function contextualAttributeRefusal(el, name, value, origin) {
2916
+ const local = el.localName;
2917
+ if (local === "meta") return metaRefusal(el, name, value, origin);
2918
+ if (local === "link") return stylesheetRefusal(el, name, value, origin);
2919
+ if (local === "script") {
2920
+ if (origin === "static" || value === null) return ContextualRefusal.None;
2921
+ return SCRIPT_SOURCE_ATTRIBUTES.has(canonicalAttrName(name)) ? ContextualRefusal.ScriptSource : ContextualRefusal.None;
2922
+ }
2923
+ if (SVG_ANIMATION_ELEMENTS.has(local) && el.namespaceURI === SVG_NS2) return animationRefusal(name, value);
2924
+ return ContextualRefusal.None;
2925
+ }
2926
+ function settleAttributeProvenance(el, name, origin) {
2927
+ if (el.localName !== "link" || canonicalAttrName(name) !== "href") return;
2928
+ if (origin !== "static" && el.hasAttribute("href")) runtimeHrefLinks.add(el);
2929
+ else runtimeHrefLinks.delete(el);
2930
+ }
2931
+
2932
+ // src/utils/setSafeAttribute.ts
2933
+ var HTML_NS2 = "http://www.w3.org/1999/xhtml";
2934
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
1742
2935
  var XLINK_NS = "http://www.w3.org/1999/xlink";
1743
2936
  var XML_NS = "http://www.w3.org/XML/1998/namespace";
1744
2937
  var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
1745
2938
  function isHtmlElement(el) {
1746
- return el.namespaceURI === null || el.namespaceURI === HTML_NS;
2939
+ return el.namespaceURI === null || el.namespaceURI === HTML_NS2;
2940
+ }
2941
+ function warnContextualRefusal(label2, name, refusal) {
2942
+ devWarnLazy(() => {
2943
+ const reasons = [
2944
+ "",
2945
+ 'the element would become a <meta http-equiv="refresh"> directive the shared refresh policy forbids (a destination outside the URL allowlist, or a directive too malformed to read unambiguously)',
2946
+ "a <meta> whose http-equiv or content is reactive may never carry a refresh directive: a browser schedules the navigation as soon as the directive is valid, and nothing can withdraw it when the state changes back",
2947
+ "a runtime value may not choose the program a <script> runs; name the script in static template source, or load it through Head({ script }) as an explicit trust decision",
2948
+ "an SVG animation may not target a URL, event-handler or nested-document attribute; its to / values would be written into that attribute without passing the URL policy",
2949
+ 'a runtime-chosen href may not become an applied <link rel="stylesheet">: a well-formed URL is not a trusted stylesheet. Name the stylesheet in static template source, or load a runtime-chosen one through Head({ link }) as an explicit trust decision'
2950
+ ];
2951
+ return `${label2}: refusing "${name}" in this element's context \u2014 ${reasons[refusal]}. The write was not performed.`;
2952
+ });
1747
2953
  }
1748
2954
  function setProp(el, key, val) {
1749
2955
  el[key] = val;
1750
2956
  }
1751
2957
  function namespaceFor(el, name) {
1752
- if (el.namespaceURI !== SVG_NS) return null;
2958
+ if (el.namespaceURI !== SVG_NS3) return null;
1753
2959
  const lower = name.toLowerCase();
1754
2960
  if (lower.startsWith("xlink:")) return XLINK_NS;
1755
2961
  if (lower.startsWith("xml:")) return XML_NS;
1756
2962
  return null;
1757
2963
  }
1758
2964
  function setSafeAttribute(el, name, value, options = {}) {
2965
+ const written = commitSafeAttribute(el, name, value, options);
2966
+ if (el.localName === "link") settleAttributeProvenance(el, name, options.reactive ? "reactive" : "runtime");
2967
+ return written;
2968
+ }
2969
+ function commitSafeAttribute(el, name, value, options) {
1759
2970
  const ns = namespaceFor(el, name);
1760
2971
  const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
1761
2972
  if (isEventHandlerAttr(name)) {
@@ -1768,6 +2979,14 @@ function setSafeAttribute(el, name, value, options = {}) {
1768
2979
  else el.removeAttribute(name);
1769
2980
  return false;
1770
2981
  }
2982
+ const pending = value == null || value === false ? null : value === true ? "" : String(value);
2983
+ const refusal = contextualAttributeRefusal(el, name, pending, options.reactive ? "reactive" : "runtime");
2984
+ if (refusal !== ContextualRefusal.None) {
2985
+ warnContextualRefusal(options.label ?? "setSafeAttribute", name, refusal);
2986
+ if (ns) el.removeAttributeNS(ns, localName);
2987
+ else el.removeAttribute(name);
2988
+ return false;
2989
+ }
1771
2990
  if (value == null) {
1772
2991
  if (ns) el.removeAttributeNS(ns, localName);
1773
2992
  else el.removeAttribute(name);
@@ -1811,13 +3030,32 @@ function setSafeAttribute(el, name, value, options = {}) {
1811
3030
  setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
1812
3031
  return true;
1813
3032
  }
1814
- const safe = sanitizeAttributeString(name, str, { element: el });
3033
+ const safe = str === "" ? "" : resolveAttributeValue(name, str, { element: el });
3034
+ if (safe === null) {
3035
+ if (ns) el.removeAttributeNS(ns, localName);
3036
+ else el.removeAttribute(name);
3037
+ return true;
3038
+ }
1815
3039
  const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
1816
3040
  if (current === safe) return true;
1817
3041
  if (ns) el.setAttributeNS(ns, name, safe);
1818
3042
  else el.setAttribute(name, safe);
1819
3043
  return true;
1820
3044
  }
3045
+ function setTrustedAttribute(el, name, value) {
3046
+ if (isHtmlContentAttribute(name)) {
3047
+ if (DEV) devWarn(`html: refusing static attribute "${name}" \u2014 the browser parses it as a nested document.`);
3048
+ return false;
3049
+ }
3050
+ const refusal = contextualAttributeRefusal(el, name, value, "static");
3051
+ if (refusal !== ContextualRefusal.None) {
3052
+ warnContextualRefusal("html (static attribute)", name, refusal);
3053
+ return false;
3054
+ }
3055
+ el.setAttribute(name, value);
3056
+ if (el.localName === "link") settleAttributeProvenance(el, name, "static");
3057
+ return true;
3058
+ }
1821
3059
 
1822
3060
  // src/reactivity/track-core.ts
1823
3061
  var POOL_MAX = 4096;
@@ -2310,7 +3548,7 @@ function forEachSubscriber(signal2, visit) {
2310
3548
  }
2311
3549
 
2312
3550
  // src/reactivity/track.ts
2313
- var _runtimeVersion = true ? "4.6.0" : "dev";
3551
+ var _runtimeVersion = true ? "4.8.0" : "dev";
2314
3552
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
2315
3553
  function resolveReactiveApi() {
2316
3554
  const g2 = globalThis;
@@ -2381,7 +3619,7 @@ function bindAttribute(el, attr, getter) {
2381
3619
  reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
2382
3620
  return;
2383
3621
  }
2384
- setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
3622
+ setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")`, reactive: true });
2385
3623
  }
2386
3624
  return reactiveBinding2(commit, el);
2387
3625
  }
@@ -2406,7 +3644,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
2406
3644
  el.removeAttribute(prevName);
2407
3645
  prevName = null;
2408
3646
  }
2409
- if (!setSafeAttribute(el, name, value, { label: "bindDynamic" })) return;
3647
+ if (!setSafeAttribute(el, name, value, { label: "bindDynamic", reactive: true })) return;
2410
3648
  prevName = name;
2411
3649
  }
2412
3650
  const teardown = reactiveBinding2(commit, el);
@@ -2587,11 +3825,7 @@ function bindChildNode(placeholder, getter) {
2587
3825
  }
2588
3826
 
2589
3827
  // src/core/rendering/tagFactory.ts
2590
- var SVG_NS2 = "http://www.w3.org/2000/svg";
2591
- var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
2592
- function isBlockedTag(tag) {
2593
- return BLOCKED_TAGS.has(tag.toLowerCase());
2594
- }
3828
+ var SVG_NS4 = "http://www.w3.org/2000/svg";
2595
3829
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
2596
3830
  "config",
2597
3831
  "location",
@@ -2618,8 +3852,35 @@ function toKebab(prop) {
2618
3852
  }
2619
3853
  function applyStyle(el, style2) {
2620
3854
  if (typeof style2 === "function") {
3855
+ const getter = style2;
3856
+ let written = [];
3857
+ let wroteString = false;
2621
3858
  const teardown = reactiveBinding2(() => {
2622
- el.setAttribute("style", sanitizeStyleAttribute(String(style2()), { element: el }));
3859
+ const value = getter();
3860
+ if (value === null || typeof value !== "object") {
3861
+ written = [];
3862
+ wroteString = true;
3863
+ el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
3864
+ return;
3865
+ }
3866
+ const decl = el.style;
3867
+ if (wroteString) {
3868
+ el.removeAttribute("style");
3869
+ wroteString = false;
3870
+ }
3871
+ const next = [];
3872
+ for (const prop in value) {
3873
+ if (!Object.hasOwn(value, prop)) continue;
3874
+ const val = value[prop];
3875
+ if (val == null) continue;
3876
+ const name = toKebab(prop);
3877
+ next.push(name);
3878
+ decl.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
3879
+ }
3880
+ for (let i2 = 0; i2 < written.length; i2++) {
3881
+ if (next.indexOf(written[i2]) === -1) decl.removeProperty(written[i2]);
3882
+ }
3883
+ written = next;
2623
3884
  }, el);
2624
3885
  registerDisposer(el, teardown);
2625
3886
  return;
@@ -2630,16 +3891,17 @@ function applyStyle(el, style2) {
2630
3891
  }
2631
3892
  const htmlEl = el;
2632
3893
  for (const prop in style2) {
3894
+ if (!Object.hasOwn(style2, prop)) continue;
2633
3895
  const val = style2[prop];
2634
3896
  const name = toKebab(prop);
2635
3897
  if (typeof val === "function") {
2636
3898
  const getter = val;
2637
3899
  const teardown = reactiveBinding2(() => {
2638
- htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter()), { property: name, element: el }));
3900
+ htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(getter()), { element: el }));
2639
3901
  }, el);
2640
3902
  registerDisposer(el, teardown);
2641
3903
  } else {
2642
- htmlEl.style.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
3904
+ htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
2643
3905
  }
2644
3906
  }
2645
3907
  }
@@ -2650,7 +3912,9 @@ function applyClass(el, cls) {
2650
3912
  }
2651
3913
  if (typeof cls === "function") {
2652
3914
  const teardown = reactiveBinding2(() => {
2653
- el.setAttribute("class", cls());
3915
+ const value = cls();
3916
+ if (value == null || value === false) el.removeAttribute("class");
3917
+ else el.setAttribute("class", value);
2654
3918
  }, el);
2655
3919
  registerDisposer(el, teardown);
2656
3920
  return;
@@ -2659,6 +3923,7 @@ function applyClass(el, cls) {
2659
3923
  let hasReactive = false;
2660
3924
  let result = "";
2661
3925
  for (const name in obj) {
3926
+ if (!Object.hasOwn(obj, name)) continue;
2662
3927
  const val = obj[name];
2663
3928
  if (typeof val === "function") {
2664
3929
  hasReactive = true;
@@ -2670,6 +3935,7 @@ function applyClass(el, cls) {
2670
3935
  const update = () => {
2671
3936
  let r = "";
2672
3937
  for (const name in obj) {
3938
+ if (!Object.hasOwn(obj, name)) continue;
2673
3939
  const val = obj[name];
2674
3940
  const active = typeof val === "function" ? val() : val;
2675
3941
  if (active) r = r ? `${r} ${name}` : name;
@@ -2732,8 +3998,8 @@ function appendChildren(el, nodes) {
2732
3998
  }
2733
3999
  }
2734
4000
  }
2735
- var tagFactory = (tag, ns) => {
2736
- const blocked = isBlockedTag(tag);
4001
+ function tagFactory(tag, ns) {
4002
+ const blocked = isBlockedElement(tag);
2737
4003
  return (first, second) => {
2738
4004
  if (blocked) {
2739
4005
  throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
@@ -2781,7 +4047,9 @@ var tagFactory = (tag, ns) => {
2781
4047
  const pClass = props.class;
2782
4048
  if (pClass != null) applyClass(el, pClass);
2783
4049
  const pId = props.id;
2784
- if (pId != null) {
4050
+ if (typeof pId === "function") {
4051
+ registerDisposer(el, bindAttribute(el, "id", pId));
4052
+ } else if (pId != null) {
2785
4053
  if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2786
4054
  devWarn(
2787
4055
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
@@ -2794,6 +4062,7 @@ var tagFactory = (tag, ns) => {
2794
4062
  const pOn = props.on;
2795
4063
  if (pOn) {
2796
4064
  for (const ev in pOn) {
4065
+ if (!Object.hasOwn(pOn, ev)) continue;
2797
4066
  const handler = pOn[ev];
2798
4067
  if (typeof handler === "function") {
2799
4068
  el.addEventListener(ev, handler);
@@ -2820,6 +4089,7 @@ var tagFactory = (tag, ns) => {
2820
4089
  continue;
2821
4090
  // already handled above / below
2822
4091
  default: {
4092
+ if (!Object.hasOwn(props, key)) continue;
2823
4093
  const value = props[key];
2824
4094
  if (value == null) continue;
2825
4095
  if (isEventHandlerAttr(key)) continue;
@@ -2831,12 +4101,10 @@ var tagFactory = (tag, ns) => {
2831
4101
  }
2832
4102
  }
2833
4103
  }
2834
- if (props.onElement && typeof props.onElement === "function") {
2835
- props.onElement(el);
2836
- }
4104
+ if (typeof props.onElement === "function") props.onElement(el);
2837
4105
  return el;
2838
4106
  };
2839
- };
4107
+ }
2840
4108
 
2841
4109
  // src/core/rendering/html.ts
2842
4110
  var html = tagFactory("html");
@@ -2911,7 +4179,7 @@ var param = tagFactory("param");
2911
4179
  var picture = tagFactory("picture");
2912
4180
  var portal = tagFactory("portal");
2913
4181
  var source = tagFactory("source");
2914
- var svg = tagFactory("svg", SVG_NS2);
4182
+ var svg = tagFactory("svg", SVG_NS4);
2915
4183
  var math = tagFactory("math");
2916
4184
  var canvas = tagFactory("canvas");
2917
4185
  var noscript = tagFactory("noscript");
@@ -2952,26 +4220,26 @@ var base = tagFactory("base");
2952
4220
  var link = tagFactory("link");
2953
4221
  var meta = tagFactory("meta");
2954
4222
  var style = tagFactory("style");
2955
- var circle = tagFactory("circle", SVG_NS2);
2956
- var ellipse = tagFactory("ellipse", SVG_NS2);
2957
- var g = tagFactory("g", SVG_NS2);
2958
- var line = tagFactory("line", SVG_NS2);
2959
- var path = tagFactory("path", SVG_NS2);
2960
- var polygon = tagFactory("polygon", SVG_NS2);
2961
- var polyline = tagFactory("polyline", SVG_NS2);
2962
- var rect = tagFactory("rect", SVG_NS2);
2963
- var text = tagFactory("text", SVG_NS2);
2964
- var tspan = tagFactory("tspan", SVG_NS2);
2965
- var defs = tagFactory("defs", SVG_NS2);
2966
- var clipPath = tagFactory("clipPath", SVG_NS2);
2967
- var mask = tagFactory("mask", SVG_NS2);
2968
- var pattern = tagFactory("pattern", SVG_NS2);
2969
- var linearGradient = tagFactory("linearGradient", SVG_NS2);
2970
- var radialGradient = tagFactory("radialGradient", SVG_NS2);
2971
- var stop = tagFactory("stop", SVG_NS2);
2972
- var use = tagFactory("use", SVG_NS2);
2973
- var symbol = tagFactory("symbol", SVG_NS2);
2974
- var marker = tagFactory("marker", SVG_NS2);
4223
+ var circle = tagFactory("circle", SVG_NS4);
4224
+ var ellipse = tagFactory("ellipse", SVG_NS4);
4225
+ var g = tagFactory("g", SVG_NS4);
4226
+ var line = tagFactory("line", SVG_NS4);
4227
+ var path = tagFactory("path", SVG_NS4);
4228
+ var polygon = tagFactory("polygon", SVG_NS4);
4229
+ var polyline = tagFactory("polyline", SVG_NS4);
4230
+ var rect = tagFactory("rect", SVG_NS4);
4231
+ var text = tagFactory("text", SVG_NS4);
4232
+ var tspan = tagFactory("tspan", SVG_NS4);
4233
+ var defs = tagFactory("defs", SVG_NS4);
4234
+ var clipPath = tagFactory("clipPath", SVG_NS4);
4235
+ var mask = tagFactory("mask", SVG_NS4);
4236
+ var pattern = tagFactory("pattern", SVG_NS4);
4237
+ var linearGradient = tagFactory("linearGradient", SVG_NS4);
4238
+ var radialGradient = tagFactory("radialGradient", SVG_NS4);
4239
+ var stop = tagFactory("stop", SVG_NS4);
4240
+ var use = tagFactory("use", SVG_NS4);
4241
+ var symbol = tagFactory("symbol", SVG_NS4);
4242
+ var marker = tagFactory("marker", SVG_NS4);
2975
4243
  var center = tagFactory("center");
2976
4244
  var font = tagFactory("font");
2977
4245
  var marquee = tagFactory("marquee");
@@ -3085,10 +4353,13 @@ function safeCall(cb, hookName) {
3085
4353
  return void 0;
3086
4354
  }
3087
4355
  }
3088
- function runMountCallback(callback, hookName, element) {
4356
+ function runMountCallback(callback, hookName, element, isDisposed) {
3089
4357
  const cleanup3 = safeCall(callback, hookName);
3090
- if (typeof cleanup3 === "function" && element) {
3091
- registerDisposer(element, cleanup3);
4358
+ if (typeof cleanup3 !== "function") return;
4359
+ if (isDisposed() || !element.isConnected) {
4360
+ safeCall(cleanup3, "onUnmount");
4361
+ } else {
4362
+ onUnmount(cleanup3, element);
3092
4363
  }
3093
4364
  }
3094
4365
  var mountWatchers = /* @__PURE__ */ new WeakMap();
@@ -3250,25 +4521,26 @@ function onMount(callback, element) {
3250
4521
  if (typeof document === "undefined") return;
3251
4522
  if (element) {
3252
4523
  let disposed = false;
4524
+ const isDisposed = () => disposed;
3253
4525
  registerDisposer(element, () => {
3254
4526
  disposed = true;
3255
4527
  });
3256
4528
  if (element.isConnected) {
3257
4529
  queueMicrotask(() => {
3258
4530
  if (disposed) return;
3259
- runMountCallback(callback, "onMount", element);
4531
+ runMountCallback(callback, "onMount", element, isDisposed);
3260
4532
  });
3261
4533
  return;
3262
4534
  }
3263
4535
  queueMicrotask(() => {
3264
4536
  if (disposed) return;
3265
4537
  if (element.isConnected) {
3266
- runMountCallback(callback, "onMount", element);
4538
+ runMountCallback(callback, "onMount", element, isDisposed);
3267
4539
  return;
3268
4540
  }
3269
4541
  const unregister = registerMountWatcher(element, () => {
3270
4542
  if (disposed) return;
3271
- runMountCallback(callback, "onMount", element);
4543
+ runMountCallback(callback, "onMount", element, isDisposed);
3272
4544
  });
3273
4545
  registerDisposer(element, unregister);
3274
4546
  });
@@ -3305,6 +4577,14 @@ function onCleanup(callback, element) {
3305
4577
  registerDisposer(element, callback);
3306
4578
  }
3307
4579
 
4580
+ // src/core/devtoolsHook.ts
4581
+ function emitDevtools(hook, event, payload) {
4582
+ try {
4583
+ hook.emit(event, payload);
4584
+ } catch {
4585
+ }
4586
+ }
4587
+
3308
4588
  // src/core/ssr-context.ts
3309
4589
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
3310
4590
  function detectSSRShared() {
@@ -3542,12 +4822,17 @@ function effect(effectFn, options) {
3542
4822
  try {
3543
4823
  retrack2(ctx.bodyFn, ctx.subscriber);
3544
4824
  if (ctx.rerunPending) drainReruns(ctx);
4825
+ } catch (err) {
4826
+ ctx.disposed = true;
4827
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
4828
+ cleanup2(ctx.subscriber);
4829
+ throw err;
3545
4830
  } finally {
3546
4831
  ctx.running = false;
3547
4832
  ctx.rerunPending = false;
3548
4833
  }
3549
4834
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3550
- if (hook) hook.emit("effect:create", { effectFn });
4835
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
3551
4836
  return () => disposeEffect(ctx);
3552
4837
  }
3553
4838
 
@@ -3634,7 +4919,7 @@ function signal(initial, options) {
3634
4919
  state2.__v++;
3635
4920
  if (DEV) {
3636
4921
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3637
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4922
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3638
4923
  }
3639
4924
  if (!enqueueBatchedSignal(state2)) {
3640
4925
  notifySubscribers2(state2);
@@ -3648,7 +4933,7 @@ function signal(initial, options) {
3648
4933
  state2.value = newValue;
3649
4934
  state2.__v++;
3650
4935
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3651
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4936
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3652
4937
  if (!enqueueBatchedSignal(state2)) {
3653
4938
  notifySubscribers2(state2);
3654
4939
  }
@@ -3667,7 +4952,7 @@ function signal(initial, options) {
3667
4952
  }
3668
4953
  if (DEV) {
3669
4954
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3670
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
4955
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
3671
4956
  }
3672
4957
  return [get, set];
3673
4958
  }
@@ -4480,36 +5765,34 @@ function Loading(props = {}) {
4480
5765
  const { text: text2, variant = "spinner", size = "md" } = props;
4481
5766
  const sizeClass = size !== "md" ? ` sibu-loading-${size}` : "";
4482
5767
  if (variant === "dots") {
4483
- return div({
5768
+ return div(
5769
+ {
5770
+ class: `sibu-loading${sizeClass}`,
5771
+ role: "status",
5772
+ "aria-live": "polite",
5773
+ // When there's no visible text, give the live region an accessible name
5774
+ // so it isn't announced as an empty status.
5775
+ "aria-label": text2 ? void 0 : "Loading"
5776
+ },
5777
+ [
5778
+ div({ class: "sibu-loading-dots" }, [
5779
+ span({ class: "sibu-loading-dot" }),
5780
+ span({ class: "sibu-loading-dot" }),
5781
+ span({ class: "sibu-loading-dot" })
5782
+ ]),
5783
+ text2 ? span({ class: "sibu-loading-text" }, text2) : null
5784
+ ]
5785
+ );
5786
+ }
5787
+ return div(
5788
+ {
4484
5789
  class: `sibu-loading${sizeClass}`,
4485
5790
  role: "status",
4486
5791
  "aria-live": "polite",
4487
- // When there's no visible text, give the live region an accessible name
4488
- // so it isn't announced as an empty status.
4489
- "aria-label": text2 ? void 0 : "Loading",
4490
- nodes: [
4491
- div({
4492
- class: "sibu-loading-dots",
4493
- nodes: [
4494
- span({ class: "sibu-loading-dot" }),
4495
- span({ class: "sibu-loading-dot" }),
4496
- span({ class: "sibu-loading-dot" })
4497
- ]
4498
- }),
4499
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4500
- ].filter(Boolean)
4501
- });
4502
- }
4503
- return div({
4504
- class: `sibu-loading${sizeClass}`,
4505
- role: "status",
4506
- "aria-live": "polite",
4507
- "aria-label": text2 ? void 0 : "Loading",
4508
- nodes: [
4509
- div({ class: "sibu-loading-spinner" }),
4510
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4511
- ].filter(Boolean)
4512
- });
5792
+ "aria-label": text2 ? void 0 : "Loading"
5793
+ },
5794
+ [div({ class: "sibu-loading-spinner" }), text2 ? span({ class: "sibu-loading-text" }, text2) : null]
5795
+ );
4513
5796
  }
4514
5797
 
4515
5798
  // src/core/rendering/action.ts
@@ -4615,13 +5898,35 @@ registerAction("copyOnClick", copyOnClick);
4615
5898
  registerAction("autoResize", autoResize);
4616
5899
  registerAction("trapFocus", trapFocus);
4617
5900
 
5901
+ // src/utils/adoptThenable.ts
5902
+ function adoptThenable(value) {
5903
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
5904
+ let then;
5905
+ try {
5906
+ then = value.then;
5907
+ } catch (error) {
5908
+ return Promise.reject(error);
5909
+ }
5910
+ if (typeof then !== "function") return null;
5911
+ return new Promise((resolve, reject) => {
5912
+ queueMicrotask(() => {
5913
+ try {
5914
+ Reflect.apply(then, value, [resolve, reject]);
5915
+ } catch (error) {
5916
+ reject(error);
5917
+ }
5918
+ });
5919
+ });
5920
+ }
5921
+
4618
5922
  // src/core/rendering/catch.ts
4619
5923
  var globalErrorHandler = null;
4620
5924
  function catchError(fn, onError) {
4621
5925
  try {
4622
5926
  const result = fn();
4623
- if (result && typeof result.then === "function") {
4624
- result.catch((err) => {
5927
+ const adopted = adoptThenable(result);
5928
+ if (adopted) {
5929
+ adopted.then(void 0, (err) => {
4625
5930
  if (onError) {
4626
5931
  onError(err, "async");
4627
5932
  } else if (globalErrorHandler) {
@@ -4748,6 +6053,10 @@ function attachBranch(parent, anchor, branch, attachedOnce, where) {
4748
6053
  parent.insertBefore(node, anchor.nextSibling);
4749
6054
  return { node, owned };
4750
6055
  }
6056
+ function releaseBranchNode(node, owned) {
6057
+ if (owned) dispose(node);
6058
+ node.parentNode?.removeChild(node);
6059
+ }
4751
6060
  function when(condition, thenBranch, elseBranch) {
4752
6061
  const anchor = document.createComment("when");
4753
6062
  let currentNode = null;
@@ -4761,16 +6070,15 @@ function when(condition, thenBranch, elseBranch) {
4761
6070
  const show2 = condition();
4762
6071
  const parent = anchor.parentNode;
4763
6072
  if (!parent) return;
4764
- if (initialized && show2 === lastCondition) return;
6073
+ if (initialized && Object.is(show2, lastCondition)) return;
4765
6074
  lastCondition = show2;
4766
6075
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
4767
6076
  if (currentNode?.parentNode) {
4768
- if (currentNodeOwned) dispose(currentNode);
4769
- currentNode.parentNode.removeChild(currentNode);
6077
+ releaseBranchNode(currentNode, currentNodeOwned);
4770
6078
  currentNode = null;
4771
6079
  }
4772
6080
  const branch = show2 ? thenBranch : elseBranch !== void 0 ? elseBranch : null;
4773
- const attached = attachBranch(parent, anchor, branch, attachedOnce, "when");
6081
+ const attached = untracked2(() => attachBranch(parent, anchor, branch, attachedOnce, "when"));
4774
6082
  currentNode = attached.node;
4775
6083
  currentNodeOwned = attached.owned;
4776
6084
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "when");
@@ -4780,6 +6088,8 @@ function when(condition, thenBranch, elseBranch) {
4780
6088
  registerDisposer(anchor, () => {
4781
6089
  disposed = true;
4782
6090
  stopBinding();
6091
+ if (currentNode) releaseBranchNode(currentNode, currentNodeOwned);
6092
+ currentNode = null;
4783
6093
  });
4784
6094
  if (!initialized) {
4785
6095
  queueMicrotask(() => {
@@ -4805,12 +6115,11 @@ function match(value, cases, fallback) {
4805
6115
  lastKey = key;
4806
6116
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
4807
6117
  if (currentNode?.parentNode) {
4808
- if (currentNodeOwned) dispose(currentNode);
4809
- currentNode.parentNode.removeChild(currentNode);
6118
+ releaseBranchNode(currentNode, currentNodeOwned);
4810
6119
  currentNode = null;
4811
6120
  }
4812
6121
  const branch = Object.hasOwn(cases, key) ? cases[key] : fallback;
4813
- const attached = attachBranch(parent, anchor, branch ?? null, attachedOnce, "match");
6122
+ const attached = untracked2(() => attachBranch(parent, anchor, branch ?? null, attachedOnce, "match"));
4814
6123
  currentNode = attached.node;
4815
6124
  currentNodeOwned = attached.owned;
4816
6125
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "match");
@@ -4820,6 +6129,8 @@ function match(value, cases, fallback) {
4820
6129
  registerDisposer(anchor, () => {
4821
6130
  disposed = true;
4822
6131
  stopBinding();
6132
+ if (currentNode) releaseBranchNode(currentNode, currentNodeOwned);
6133
+ currentNode = null;
4823
6134
  });
4824
6135
  if (!initialized) {
4825
6136
  queueMicrotask(() => {
@@ -4840,11 +6151,17 @@ var componentRegistry = globalSingleton(
4840
6151
  /* @__PURE__ */ Symbol.for("sibujs.components.registry.v1"),
4841
6152
  () => /* @__PURE__ */ new Map()
4842
6153
  );
6154
+ var registryVersion = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.components.registryVersion.v1"), () => signal(0));
6155
+ function bumpRegistry() {
6156
+ const [read, write] = registryVersion;
6157
+ write(untracked2(read) + 1);
6158
+ }
4843
6159
  function registerComponent(name, component) {
4844
6160
  componentRegistry.set(name, component);
6161
+ bumpRegistry();
4845
6162
  }
4846
6163
  function unregisterComponent(name) {
4847
- componentRegistry.delete(name);
6164
+ if (componentRegistry.delete(name)) bumpRegistry();
4848
6165
  }
4849
6166
  function resolveComponent(name) {
4850
6167
  const component = componentRegistry.get(name);
@@ -4855,14 +6172,21 @@ function resolveComponent(name) {
4855
6172
  }
4856
6173
  function DynamicComponent(is) {
4857
6174
  const container = div({ class: "sibu-dynamic" });
6175
+ const NONE = {};
6176
+ let shown = NONE;
4858
6177
  function render() {
4859
6178
  const target = is();
4860
- let el;
6179
+ let component;
4861
6180
  if (typeof target === "function") {
4862
- el = target();
6181
+ component = target;
4863
6182
  } else {
4864
- el = resolveComponent(target);
6183
+ registryVersion[0]();
6184
+ component = componentRegistry.get(target);
4865
6185
  }
6186
+ const next = component ?? `missing:${target}`;
6187
+ if (next === shown) return;
6188
+ const el = untracked2(() => component ? component() : div(`[Component "${target}" not found]`));
6189
+ shown = next;
4866
6190
  replaceChildrenSafely(container, el);
4867
6191
  }
4868
6192
  const untrack = track2(render);
@@ -5126,31 +6450,29 @@ function Fragment(nodes) {
5126
6450
  if (Array.isArray(child)) {
5127
6451
  for (const nested of child) {
5128
6452
  if (nested == null || typeof nested === "boolean") continue;
5129
- frag.appendChild(resolveChild(nested));
6453
+ appendChild(frag, nested);
5130
6454
  }
5131
6455
  } else {
5132
- frag.appendChild(resolveChild(child));
6456
+ appendChild(frag, child);
5133
6457
  }
5134
6458
  }
5135
6459
  return frag;
5136
6460
  }
5137
- function resolveChild(child) {
5138
- if (child == null) {
5139
- return document.createTextNode("");
5140
- }
6461
+ function appendChild(frag, child) {
5141
6462
  if (child instanceof Node) {
5142
- return child;
6463
+ frag.appendChild(child);
6464
+ return;
5143
6465
  }
5144
6466
  if (typeof child === "function") {
5145
- const result = child();
5146
- if (result instanceof Node) return result;
5147
- return document.createTextNode(String(result ?? ""));
6467
+ const placeholder = document.createComment("");
6468
+ frag.appendChild(placeholder);
6469
+ registerDisposer(placeholder, bindChildNode(placeholder, child));
6470
+ return;
5148
6471
  }
5149
- return document.createTextNode(String(child));
6472
+ frag.appendChild(document.createTextNode(String(child)));
5150
6473
  }
5151
6474
 
5152
6475
  // src/core/rendering/htm.ts
5153
- var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
5154
6476
  var VOID_ELEMENTS3 = /* @__PURE__ */ new Set([
5155
6477
  "area",
5156
6478
  "base",
@@ -5191,7 +6513,7 @@ var SVG_TAGS2 = /* @__PURE__ */ new Set([
5191
6513
  "marker"
5192
6514
  ]);
5193
6515
  var cache = /* @__PURE__ */ new WeakMap();
5194
- function parseTemplate(strings) {
6516
+ function parseTemplate2(strings) {
5195
6517
  const exprCount = strings.length - 1;
5196
6518
  let template2 = strings[0];
5197
6519
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -5373,7 +6695,7 @@ function parseTemplate(strings) {
5373
6695
  children.push({ t: 0, el: { tag, svg: SVG_TAGS2.has(tag), attrs, children: [] } });
5374
6696
  } else {
5375
6697
  const inner = parseChildren();
5376
- if (RAW_TEXT_TAGS.has(tag.toLowerCase())) {
6698
+ if (isCodeTextElement(tag)) {
5377
6699
  for (let i2 = 0; i2 < inner.length; i2++) {
5378
6700
  if (inner[i2].t === 2) {
5379
6701
  throw new Error(
@@ -5399,12 +6721,12 @@ function parseTemplate(strings) {
5399
6721
  return parseChildren();
5400
6722
  }
5401
6723
  function executeElement(tmpl, values) {
5402
- const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
6724
+ const el = tmpl.svg ? document.createElementNS(SVG_NS4, tmpl.tag) : document.createElement(tmpl.tag);
5403
6725
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
5404
6726
  const attr = tmpl.attrs[i2];
5405
6727
  switch (attr.t) {
5406
6728
  case 0:
5407
- el.setAttribute(attr.name, attr.value);
6729
+ setTrustedAttribute(el, attr.name, attr.value);
5408
6730
  break;
5409
6731
  case 1: {
5410
6732
  const name = attr.name;
@@ -5437,7 +6759,7 @@ function executeElement(tmpl, values) {
5437
6759
  break;
5438
6760
  }
5439
6761
  case 4:
5440
- el.setAttribute(attr.name, "");
6762
+ setTrustedAttribute(el, attr.name, "");
5441
6763
  break;
5442
6764
  }
5443
6765
  }
@@ -5479,9 +6801,15 @@ function executeElement(tmpl, values) {
5479
6801
  function html2(strings, ...values) {
5480
6802
  let tmpl = cache.get(strings);
5481
6803
  if (!tmpl) {
5482
- tmpl = parseTemplate(strings);
6804
+ tmpl = parseTemplate2(strings);
5483
6805
  cache.set(strings, tmpl);
5484
6806
  }
6807
+ return renderTemplate(tmpl, values);
6808
+ }
6809
+ function __renderParsedTemplate(tree, values) {
6810
+ return renderTemplate(tree, values);
6811
+ }
6812
+ function renderTemplate(tmpl, values) {
5485
6813
  if (tmpl.length === 1 && tmpl[0].t === 0) {
5486
6814
  return executeElement(tmpl[0].el, values);
5487
6815
  }
@@ -5616,25 +6944,77 @@ function mount(component, container) {
5616
6944
  typeof component === "function" || component instanceof Node,
5617
6945
  "mount: first argument must be a component function or a DOM Node."
5618
6946
  );
5619
- const startTime = typeof performance !== "undefined" ? performance.now() : 0;
5620
- const node = typeof component === "function" ? component() : component;
5621
- const duration = typeof performance !== "undefined" ? performance.now() - startTime : 0;
5622
- container.appendChild(node);
5623
- const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5624
- if (hook) {
5625
- hook.emit("app:init", { rootElement: node, container, duration });
6947
+ const now = () => typeof performance !== "undefined" ? performance.now() : 0;
6948
+ const startTime = now();
6949
+ let range = null;
6950
+ const attach = (built) => {
6951
+ if (built instanceof DocumentFragment) {
6952
+ const start = document.createComment("");
6953
+ const end = document.createComment("");
6954
+ built.insertBefore(start, built.firstChild);
6955
+ built.appendChild(end);
6956
+ range = { start, end };
6957
+ }
6958
+ container.appendChild(built);
6959
+ };
6960
+ let node;
6961
+ if (typeof component === "function") {
6962
+ node = withDisposerRollback(() => {
6963
+ const built = component();
6964
+ attach(built);
6965
+ return built;
6966
+ });
6967
+ } else {
6968
+ node = component;
6969
+ attach(node);
5626
6970
  }
6971
+ const duration = now() - startTime;
6972
+ const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6973
+ if (hook) emitDevtools(hook, "app:init", { rootElement: node, container, duration });
6974
+ const mounted = range;
5627
6975
  return {
5628
6976
  node,
5629
6977
  unmount() {
5630
- if (hook) hook.emit("app:unmount", { rootElement: node });
5631
- dispose(node);
5632
- if (node.parentNode) {
5633
- node.parentNode.removeChild(node);
6978
+ if (hook) emitDevtools(hook, "app:unmount", { rootElement: node });
6979
+ if (mounted) {
6980
+ unmountRange(mounted.start, mounted.end);
6981
+ return;
5634
6982
  }
6983
+ dispose(node);
6984
+ node.parentNode?.removeChild(node);
5635
6985
  }
5636
6986
  };
5637
6987
  }
6988
+ function unmountRange(start, end) {
6989
+ const parent = start.parentNode;
6990
+ if (!parent && !end.parentNode) return;
6991
+ let budget = MAX_DRAIN_TEARDOWNS;
6992
+ for (let n = start.nextSibling; n && n !== end; n = n.nextSibling) budget++;
6993
+ let before = start;
6994
+ let after = null;
6995
+ let intact = true;
6996
+ for (; ; ) {
6997
+ if (start.parentNode !== parent || end.parentNode !== parent) intact = false;
6998
+ else if (start.previousSibling !== before || end.nextSibling !== after) {
6999
+ before = start.previousSibling;
7000
+ after = end.nextSibling;
7001
+ intact = !!(start.compareDocumentPosition(end) & 4);
7002
+ }
7003
+ const n = start.nextSibling;
7004
+ if (!intact || n === end) break;
7005
+ if (budget-- <= 0) {
7006
+ reportDrainRunaway("mount", MAX_DRAIN_TEARDOWNS, 1);
7007
+ return;
7008
+ }
7009
+ dispose(n);
7010
+ if (n.parentNode === parent) parent.removeChild(n);
7011
+ }
7012
+ if (!intact) {
7013
+ reportError(new Error("[SibuJS mount] fragment markers lost"), { phase: "cleanup", name: "mount" });
7014
+ }
7015
+ start.parentNode?.removeChild(start);
7016
+ end.parentNode?.removeChild(end);
7017
+ }
5638
7018
 
5639
7019
  // src/core/rendering/portal.ts
5640
7020
  function Portal(nodes, target) {
@@ -6070,17 +7450,17 @@ function derived(getter, options) {
6070
7450
  cs._d = false;
6071
7451
  cs._init = true;
6072
7452
  };
6073
- track2(() => {
6074
- let threw = true;
6075
- try {
7453
+ try {
7454
+ track2(() => {
6076
7455
  cs._v = getter();
6077
7456
  cs._d = false;
6078
7457
  cs._init = true;
6079
- threw = false;
6080
- } finally {
6081
- if (threw) cs._d = true;
6082
- }
6083
- }, markDirty);
7458
+ }, markDirty);
7459
+ } catch (err) {
7460
+ disposed = true;
7461
+ cleanup2(markDirty);
7462
+ throw err;
7463
+ }
6084
7464
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6085
7465
  const validate = () => {
6086
7466
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -6099,7 +7479,7 @@ function derived(getter, options) {
6099
7479
  if (disposed) cleanup2(markDirty);
6100
7480
  }
6101
7481
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
6102
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
7482
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
6103
7483
  }
6104
7484
  };
6105
7485
  cs._validate = validate;
@@ -6150,7 +7530,7 @@ function derived(getter, options) {
6150
7530
  }
6151
7531
  }
6152
7532
  };
6153
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
7533
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
6154
7534
  return computedGetter;
6155
7535
  }
6156
7536
 
@@ -6518,7 +7898,7 @@ function enhance(target, setup) {
6518
7898
  bindNode(el, () => {
6519
7899
  const v = value();
6520
7900
  const next = v == null ? null : String(v);
6521
- setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()" });
7901
+ setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()", reactive: true });
6522
7902
  })
6523
7903
  );
6524
7904
  });
@@ -6790,27 +8170,6 @@ function trustHTML(html3) {
6790
8170
  }
6791
8171
  var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
6792
8172
 
6793
- // src/utils/adoptThenable.ts
6794
- function adoptThenable(value) {
6795
- if (value === null || typeof value !== "object" && typeof value !== "function") return null;
6796
- let then;
6797
- try {
6798
- then = value.then;
6799
- } catch (error) {
6800
- return Promise.reject(error);
6801
- }
6802
- if (typeof then !== "function") return null;
6803
- return new Promise((resolve, reject) => {
6804
- queueMicrotask(() => {
6805
- try {
6806
- Reflect.apply(then, value, [resolve, reject]);
6807
- } catch (error) {
6808
- reject(error);
6809
- }
6810
- });
6811
- });
6812
- }
6813
-
6814
8173
  // src/reactivity/concurrent.ts
6815
8174
  function defer(getter) {
6816
8175
  const [value, setValue] = signal(getter());
@@ -6901,7 +8260,23 @@ function nextTick() {
6901
8260
  }
6902
8261
 
6903
8262
  // src/build/cdn.ts
6904
- var PACKAGE_NAME = "sibu";
8263
+ var PACKAGE_NAME = "sibujs";
8264
+ var SUBPATH_ENTRIES = [
8265
+ "data",
8266
+ "browser",
8267
+ "patterns",
8268
+ "motion",
8269
+ "ui",
8270
+ "widgets",
8271
+ "ssr",
8272
+ "devtools",
8273
+ "performance",
8274
+ "ecosystem",
8275
+ "plugins",
8276
+ "build",
8277
+ "testing",
8278
+ "extras"
8279
+ ];
6905
8280
  function registerGlobal() {
6906
8281
  if (typeof window === "undefined") return;
6907
8282
  window.Sibu = { ...index_exports };
@@ -6947,7 +8322,7 @@ var cdnUrls = {
6947
8322
  * @example
6948
8323
  * ```ts
6949
8324
  * cdnUrls.scriptTag('jsdelivr', '1.0.0')
6950
- * // => '<script src="https://cdn.jsdelivr.net/npm/sibu@1.0.0/dist/cdn.global.js"></script>'
8325
+ * // => '<script src="https://cdn.jsdelivr.net/npm/sibujs@1.0.0/dist/cdn.global.js"></script>'
6951
8326
  * ```
6952
8327
  */
6953
8328
  scriptTag: (provider = "jsdelivr", version) => {
@@ -6965,15 +8340,11 @@ function generateImportMap(baseUrl) {
6965
8340
  const base2 = baseUrl || `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@latest`;
6966
8341
  const imports = {
6967
8342
  // Main package entry
6968
- [PACKAGE_NAME]: `${base2}/dist/index.js`,
6969
- // Sub-path imports for tree-shaking in browsers
6970
- [`${PACKAGE_NAME}/core`]: `${base2}/dist/core/index.js`,
6971
- [`${PACKAGE_NAME}/reactivity`]: `${base2}/dist/reactivity/index.js`,
6972
- [`${PACKAGE_NAME}/plugins`]: `${base2}/dist/plugins/index.js`,
6973
- [`${PACKAGE_NAME}/components`]: `${base2}/dist/components/index.js`,
6974
- [`${PACKAGE_NAME}/testing`]: `${base2}/dist/testing/index.js`,
6975
- [`${PACKAGE_NAME}/build`]: `${base2}/dist/build/index.js`
8343
+ [PACKAGE_NAME]: `${base2}/dist/index.js`
6976
8344
  };
8345
+ for (const entry of SUBPATH_ENTRIES) {
8346
+ imports[`${PACKAGE_NAME}/${entry}`] = `${base2}/dist/${entry}.js`;
8347
+ }
6977
8348
  return {
6978
8349
  imports,
6979
8350
  /**
@@ -7056,9 +8427,9 @@ function generateTsConfig(options) {
7056
8427
  if (paths && Object.keys(paths).length > 0) {
7057
8428
  compilerOptions.baseUrl = ".";
7058
8429
  compilerOptions.paths = {
7059
- // Default sibu alias
7060
- sibu: ["node_modules/sibu/dist/index.d.ts"],
7061
- "sibu/*": ["node_modules/sibu/dist/*"],
8430
+ // Default sibujs alias
8431
+ sibujs: ["node_modules/sibujs/dist/index.d.ts"],
8432
+ "sibujs/*": ["node_modules/sibujs/dist/*"],
7062
8433
  // User-provided paths
7063
8434
  ...paths
7064
8435
  };
@@ -8172,6 +9543,19 @@ function generateTypeStubs() {
8172
9543
  };
8173
9544
  }
8174
9545
 
9546
+ // src/build/compileTemplates.ts
9547
+ function compileHtmlTemplates(code2) {
9548
+ const plan = planHtmlTemplates(code2);
9549
+ const edits = plan.compiledCount > 0 ? normalizeEdits(plan.edits) : null;
9550
+ return {
9551
+ code: edits ? applyEdits(code2, edits, plan.append) : null,
9552
+ usedTags: plan.usedTags,
9553
+ usesSvg: plan.usesSvg,
9554
+ compiledCount: edits ? plan.compiledCount : 0,
9555
+ skippedCount: plan.skippedCount
9556
+ };
9557
+ }
9558
+
8175
9559
  // src/build/routeSplitting.ts
8176
9560
  function fileToRoute(relativePath) {
8177
9561
  const basename = relativePath.split("/").pop() || "";
@@ -8227,7 +9611,7 @@ function generateRouteModule(entries, routesDir) {
8227
9611
  (e, i2) => `const route${i2} = () => import(/* webpackChunkName: "${e.chunkName}" */ "${routesDir}/${e.importPath}");`
8228
9612
  ).join("\n");
8229
9613
  const routes = entries.map((e, i2) => ` { path: "${e.path}", component: lazy(route${i2}) }`).join(",\n");
8230
- return `import { lazy } from "sibu";
9614
+ return `import { lazy } from "sibujs";
8231
9615
 
8232
9616
  ${imports}
8233
9617