sibujs 4.5.0 → 4.6.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 (109) hide show
  1. package/dist/browser.cjs +309 -129
  2. package/dist/browser.d.cts +46 -14
  3. package/dist/browser.d.ts +46 -14
  4. package/dist/browser.js +8 -6
  5. package/dist/build.cjs +184 -72
  6. package/dist/build.js +15 -16
  7. package/dist/cdn.dev.global.js +12 -12
  8. package/dist/cdn.full.dev.global.js +11 -11
  9. package/dist/cdn.full.global.js +10 -10
  10. package/dist/cdn.global.js +12 -12
  11. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  12. package/dist/chunk-3ISI6ACU.js +44 -0
  13. package/dist/{chunk-ONOHFDLG.js → chunk-47M47FOM.js} +3 -3
  14. package/dist/chunk-5HZXGZ6T.js +24 -0
  15. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  16. package/dist/{chunk-UGRX3S57.js → chunk-5Q4R7HCL.js} +12 -3
  17. package/dist/{chunk-S373NSMK.js → chunk-7GQCWFOE.js} +150 -59
  18. package/dist/{chunk-FTIR4QW2.js → chunk-7NCARGJW.js} +6 -8
  19. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  20. package/dist/{chunk-VZKNK2V7.js → chunk-AIF3Z2T7.js} +296 -38
  21. package/dist/{chunk-4PMLNECI.js → chunk-BAAG6ZTI.js} +98 -29
  22. package/dist/{chunk-XZZOBQAY.js → chunk-D33YTSX5.js} +2 -2
  23. package/dist/{chunk-TBYTO6BS.js → chunk-DKGBBKOF.js} +136 -80
  24. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  25. package/dist/{chunk-GW3SCCZG.js → chunk-HYXDKS4N.js} +153 -110
  26. package/dist/chunk-J6FW5TV6.js +233 -0
  27. package/dist/{chunk-PCT43HW3.js → chunk-JSXPZCET.js} +1 -1
  28. package/dist/chunk-NYNYSPK7.js +318 -0
  29. package/dist/{chunk-RJE2BNI4.js → chunk-ORMZXBKQ.js} +108 -45
  30. package/dist/{chunk-OMJJM3KM.js → chunk-OXUY2A6L.js} +287 -130
  31. package/dist/{chunk-3JZ4L5TJ.js → chunk-PK6FK2G2.js} +181 -66
  32. package/dist/{chunk-RBTPLM32.js → chunk-QE4TTDU3.js} +2 -2
  33. package/dist/{chunk-2DCGACUU.js → chunk-SXXVZMKZ.js} +251 -66
  34. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  35. package/dist/{chunk-7LN645I6.js → chunk-UZQ6ALFS.js} +3 -3
  36. package/dist/{chunk-KEISJXBU.js → chunk-VVWPJ543.js} +6 -8
  37. package/dist/{chunk-RIXRAYIU.js → chunk-XC4MEKGA.js} +3 -3
  38. package/dist/{chunk-B3WHI2QA.js → chunk-XRRZKZYX.js} +55 -31
  39. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  40. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  41. package/dist/{customElement-OB9CIsc5.d.cts → customElement-MmInOW1U.d.cts} +21 -0
  42. package/dist/{customElement-OB9CIsc5.d.ts → customElement-MmInOW1U.d.ts} +21 -0
  43. package/dist/data.cjs +213 -117
  44. package/dist/data.d.cts +47 -9
  45. package/dist/data.d.ts +47 -9
  46. package/dist/data.js +12 -9
  47. package/dist/devtools.cjs +81 -57
  48. package/dist/devtools.js +7 -8
  49. package/dist/dispose-GEIG2KOF.js +28 -0
  50. package/dist/ecosystem.cjs +309 -104
  51. package/dist/ecosystem.d.cts +19 -3
  52. package/dist/ecosystem.d.ts +19 -3
  53. package/dist/ecosystem.js +12 -12
  54. package/dist/extras.cjs +2133 -863
  55. package/dist/extras.d.cts +10 -9
  56. package/dist/extras.d.ts +10 -9
  57. package/dist/extras.js +39 -28
  58. package/dist/index.cjs +184 -72
  59. package/dist/index.d.cts +22 -5
  60. package/dist/index.d.ts +22 -5
  61. package/dist/index.js +24 -27
  62. package/dist/motion.cjs +107 -41
  63. package/dist/motion.js +5 -5
  64. package/dist/patterns.cjs +317 -39
  65. package/dist/patterns.d.cts +27 -9
  66. package/dist/patterns.d.ts +27 -9
  67. package/dist/patterns.js +8 -8
  68. package/dist/performance.cjs +308 -216
  69. package/dist/performance.d.cts +2 -2
  70. package/dist/performance.d.ts +2 -2
  71. package/dist/performance.js +8 -9
  72. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  73. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  74. package/dist/plugins.cjs +549 -202
  75. package/dist/plugins.d.cts +3 -3
  76. package/dist/plugins.d.ts +3 -3
  77. package/dist/plugins.js +35 -23
  78. package/dist/signal-EotCj4hS.d.cts +110 -0
  79. package/dist/signal-EotCj4hS.d.ts +110 -0
  80. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  81. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  82. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  83. package/dist/ssr.cjs +176 -72
  84. package/dist/ssr.d.cts +9 -3
  85. package/dist/ssr.d.ts +9 -3
  86. package/dist/ssr.js +11 -12
  87. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  88. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  89. package/dist/{tagFactory-DVoDpHye.d.ts → tagFactory-DFstCLQV.d.cts} +2 -100
  90. package/dist/{tagFactory-DVoDpHye.d.cts → tagFactory-DkaNVUNV.d.ts} +2 -100
  91. package/dist/testing.cjs +2484 -2185
  92. package/dist/testing.d.cts +56 -5
  93. package/dist/testing.d.ts +56 -5
  94. package/dist/testing.js +580 -307
  95. package/dist/ui.cjs +707 -317
  96. package/dist/ui.d.cts +40 -7
  97. package/dist/ui.d.ts +40 -7
  98. package/dist/ui.js +151 -56
  99. package/dist/widgets.cjs +246 -278
  100. package/dist/widgets.js +9 -10
  101. package/package.json +1 -1
  102. package/dist/chunk-2WLZ6757.js +0 -149
  103. package/dist/chunk-CCSJMTRN.js +0 -15
  104. package/dist/chunk-QKRPLZ2V.js +0 -108
  105. package/dist/chunk-VUF4ALSW.js +0 -60
  106. package/dist/chunk-WWV3SJ3L.js +0 -131
  107. package/dist/dispose-46BOMMQJ.js +0 -19
  108. package/dist/plugin-D30wlGW5.d.cts +0 -71
  109. package/dist/plugin-D30wlGW5.d.ts +0 -71
@@ -58,6 +58,94 @@ __export(performance_exports, {
58
58
  });
59
59
  module.exports = __toCommonJS(performance_exports);
60
60
 
61
+ // src/core/errors.ts
62
+ var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
63
+ function state() {
64
+ const g = globalThis;
65
+ const existing = g[ERROR_STATE_KEY];
66
+ if (existing) return existing;
67
+ const created = { handler: null, handlerDepth: 0 };
68
+ g[ERROR_STATE_KEY] = created;
69
+ return created;
70
+ }
71
+ function normalizeError(value) {
72
+ if (value instanceof Error) return value;
73
+ const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
74
+ return new Error(message, { cause: value });
75
+ }
76
+ function safeStringify(value) {
77
+ if (typeof value === "symbol") return value.toString();
78
+ try {
79
+ return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
80
+ } catch {
81
+ return Object.prototype.toString.call(value);
82
+ }
83
+ }
84
+ function describe(context) {
85
+ return context.name ? `${context.phase} "${context.name}"` : context.phase;
86
+ }
87
+ var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
88
+ function untrackedRun(fn) {
89
+ const api = globalThis[REACTIVE_REGISTRY_KEY];
90
+ return api?.untracked ? api.untracked(fn) : fn();
91
+ }
92
+ var PROPAGATE_EVENT = "sibu:error-propagate";
93
+ function claimedByBoundary(error, context) {
94
+ const raw = context.node;
95
+ if (!raw || typeof CustomEvent === "undefined") return false;
96
+ const target = resolveEventTarget(raw);
97
+ if (!target) return false;
98
+ try {
99
+ const event = new CustomEvent(PROPAGATE_EVENT, {
100
+ bubbles: true,
101
+ cancelable: true,
102
+ detail: { error, context }
103
+ });
104
+ return target.dispatchEvent(event) === false;
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+ function resolveEventTarget(node) {
110
+ const ELEMENT_NODE = 1;
111
+ let current = node;
112
+ while (current) {
113
+ if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
114
+ return current;
115
+ }
116
+ current = current.parentNode;
117
+ }
118
+ const self = node;
119
+ return typeof self.dispatchEvent === "function" ? self : null;
120
+ }
121
+ function defaultReport(error, context) {
122
+ if (typeof console === "undefined") return;
123
+ console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
124
+ }
125
+ function reportError(error, context) {
126
+ const err = normalizeError(error);
127
+ untrackedRun(() => {
128
+ if (claimedByBoundary(err, context)) return;
129
+ const s = state();
130
+ const handler = s.handler;
131
+ if (handler && s.handlerDepth === 0) {
132
+ s.handlerDepth++;
133
+ try {
134
+ handler(error instanceof Error ? error : err, context);
135
+ return;
136
+ } catch (handlerError) {
137
+ if (typeof console !== "undefined") {
138
+ const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
139
+ console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
140
+ }
141
+ } finally {
142
+ s.handlerDepth--;
143
+ }
144
+ }
145
+ defaultReport(err, context);
146
+ });
147
+ }
148
+
61
149
  // src/utils/globalSingleton.ts
62
150
  function globalSingleton(key, create) {
63
151
  const g = globalThis;
@@ -88,28 +176,34 @@ function insertTask(task) {
88
176
  }
89
177
  taskQueue.splice(i, 0, task);
90
178
  }
179
+ function runTask(callback) {
180
+ try {
181
+ callback();
182
+ } catch (err) {
183
+ reportError(err, { phase: "scheduler", name: "scheduleUpdate" });
184
+ }
185
+ }
91
186
  function processQueue() {
92
187
  if (_sched.isProcessing || taskQueue.length === 0) return;
93
188
  _sched.isProcessing = true;
94
189
  const startTime = performance.now();
95
190
  const timeSlice = 5;
96
- while (taskQueue.length > 0) {
97
- const task = taskQueue[0];
98
- if (task.cancelled) {
191
+ try {
192
+ while (taskQueue.length > 0) {
193
+ const task = taskQueue[0];
194
+ if (task.cancelled) {
195
+ taskQueue.shift();
196
+ continue;
197
+ }
198
+ if (task.priority > Priority.IMMEDIATE && performance.now() - startTime > timeSlice) {
199
+ break;
200
+ }
99
201
  taskQueue.shift();
100
- continue;
101
- }
102
- if (task.priority > Priority.IMMEDIATE && performance.now() - startTime > timeSlice) {
103
- break;
104
- }
105
- taskQueue.shift();
106
- try {
107
- task.callback();
108
- } catch (e) {
109
- console.error("[Scheduler] Task error:", e);
202
+ runTask(task.callback);
110
203
  }
204
+ } finally {
205
+ _sched.isProcessing = false;
111
206
  }
112
- _sched.isProcessing = false;
113
207
  if (taskQueue.length > 0) {
114
208
  scheduleFrame();
115
209
  }
@@ -177,11 +271,7 @@ function scheduleUpdate(priority, callback) {
177
271
  cancelled: false
178
272
  };
179
273
  if (priority === Priority.IMMEDIATE) {
180
- try {
181
- callback();
182
- } catch (e) {
183
- console.error("[Scheduler] Immediate task error:", e);
184
- }
274
+ runTask(callback);
185
275
  return () => {
186
276
  };
187
277
  }
@@ -201,14 +291,15 @@ function flushScheduler() {
201
291
  _sched.scheduledHandle = null;
202
292
  _sched.scheduledKind = null;
203
293
  _sched.microtaskScheduled = false;
204
- while (taskQueue.length > 0) {
205
- const task = taskQueue.shift();
206
- if (!task) break;
207
- if (!task.cancelled) {
208
- task.callback();
294
+ try {
295
+ while (taskQueue.length > 0) {
296
+ const task = taskQueue.shift();
297
+ if (!task) break;
298
+ if (!task.cancelled) runTask(task.callback);
209
299
  }
300
+ } finally {
301
+ _sched.isProcessing = false;
210
302
  }
211
- _sched.isProcessing = false;
212
303
  }
213
304
  function pendingTasks() {
214
305
  return taskQueue.filter((t) => !t.cancelled).length;
@@ -246,92 +337,99 @@ function devWarn(message) {
246
337
  }
247
338
  }
248
339
 
249
- // src/core/errors.ts
250
- var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
251
- function state() {
252
- const g = globalThis;
253
- const existing = g[ERROR_STATE_KEY];
254
- if (existing) return existing;
255
- const created = { handler: null, handlerDepth: 0 };
256
- g[ERROR_STATE_KEY] = created;
257
- return created;
258
- }
259
- function normalizeError(value) {
260
- if (value instanceof Error) return value;
261
- const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
262
- return new Error(message, { cause: value });
340
+ // src/core/rendering/dispose.ts
341
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
342
+ var MAX_DRAIN_TEARDOWNS = 1e4;
343
+ function reportDrainRunaway(label, executed, remaining) {
344
+ if (typeof console === "undefined") return;
345
+ console.error(
346
+ `[SibuJS ${label}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
347
+ { executed, remaining }
348
+ );
263
349
  }
264
- function safeStringify(value) {
265
- if (typeof value === "symbol") return value.toString();
266
- try {
267
- return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
268
- } catch {
269
- return Object.prototype.toString.call(value);
270
- }
350
+ var activeBindingCount = 0;
351
+ function resolveCapture(capture) {
352
+ let frame = capture;
353
+ while (frame?.closed) frame = frame.forwardTo;
354
+ return frame ?? null;
355
+ }
356
+ var registrationCaptures = [];
357
+ function currentDisposerCapture() {
358
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
359
+ }
360
+ function beginDisposerCapture(capture) {
361
+ if (capture === null && registrationCaptures.length === 0) return false;
362
+ registrationCaptures.push(resolveCapture(capture));
363
+ return true;
271
364
  }
272
- function describe(context) {
273
- return context.name ? `${context.phase} "${context.name}"` : context.phase;
365
+ function endDisposerCapture() {
366
+ registrationCaptures.pop();
274
367
  }
275
- var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
276
- function untrackedRun(fn) {
277
- const api = globalThis[REACTIVE_REGISTRY_KEY];
278
- return api?.untracked ? api.untracked(fn) : fn();
279
- }
280
- var PROPAGATE_EVENT = "sibu:error-propagate";
281
- function claimedByBoundary(error, context) {
282
- const raw = context.node;
283
- if (!raw || typeof CustomEvent === "undefined") return false;
284
- const target = resolveEventTarget(raw);
285
- if (!target) return false;
286
- try {
287
- const event = new CustomEvent(PROPAGATE_EVENT, {
288
- bubbles: true,
289
- cancelable: true,
290
- detail: { error, context }
291
- });
292
- return target.dispatchEvent(event) === false;
293
- } catch {
294
- return false;
368
+ function registerDisposer(node, teardown) {
369
+ let disposers = elementDisposers.get(node);
370
+ if (!disposers) {
371
+ disposers = [];
372
+ elementDisposers.set(node, disposers);
295
373
  }
374
+ disposers.push(teardown);
375
+ if (DEV) activeBindingCount++;
376
+ const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
377
+ if (capture) capture.entries.push([node, teardown]);
296
378
  }
297
- function resolveEventTarget(node) {
298
- const ELEMENT_NODE = 1;
299
- let current = node;
300
- while (current) {
301
- if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
302
- return current;
379
+ function dispose(node) {
380
+ const stack = [node];
381
+ const order = [];
382
+ while (stack.length > 0) {
383
+ const current = stack.pop();
384
+ order.push(current);
385
+ const children = Array.from(current.childNodes);
386
+ for (let i = 0; i < children.length; i++) {
387
+ stack.push(children[i]);
303
388
  }
304
- current = current.parentNode;
305
389
  }
306
- const self = node;
307
- return typeof self.dispatchEvent === "function" ? self : null;
308
- }
309
- function defaultReport(error, context) {
310
- if (typeof console === "undefined") return;
311
- console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
312
- }
313
- function reportError(error, context) {
314
- const err = normalizeError(error);
315
- untrackedRun(() => {
316
- if (claimedByBoundary(err, context)) return;
317
- const s = state();
318
- const handler = s.handler;
319
- if (handler && s.handlerDepth === 0) {
320
- s.handlerDepth++;
390
+ for (let i = order.length - 1; i >= 0; i--) {
391
+ disposeNodeOwn(order[i]);
392
+ }
393
+ }
394
+ function disposeNodeOwn(node) {
395
+ if (!elementDisposers.has(node)) return;
396
+ let executed = 0;
397
+ let runaway = false;
398
+ while (!runaway) {
399
+ const pending = elementDisposers.get(node);
400
+ if (!pending || pending.length === 0) break;
401
+ const snapshot = pending.slice();
402
+ elementDisposers.delete(node);
403
+ if (DEV) activeBindingCount -= snapshot.length;
404
+ for (let i = 0; i < snapshot.length; i++) {
405
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
406
+ const rest = snapshot.slice(i);
407
+ const added = elementDisposers.get(node);
408
+ elementDisposers.set(node, added ? rest.concat(added) : rest);
409
+ if (DEV) activeBindingCount += rest.length;
410
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
411
+ runaway = true;
412
+ break;
413
+ }
414
+ executed++;
321
415
  try {
322
- handler(error instanceof Error ? error : err, context);
323
- return;
324
- } catch (handlerError) {
325
- if (typeof console !== "undefined") {
326
- const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
327
- console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
328
- }
329
- } finally {
330
- s.handlerDepth--;
416
+ snapshot[i]();
417
+ } catch (err) {
418
+ reportError(err, { phase: "cleanup", name: "disposer" });
331
419
  }
332
420
  }
333
- defaultReport(err, context);
334
- });
421
+ }
422
+ }
423
+ function replaceChildrenSafely(parent, ...next) {
424
+ for (let i = 0; i < next.length; i++) {
425
+ const node = next[i];
426
+ node.parentNode?.removeChild(node);
427
+ }
428
+ const current = Array.from(parent.childNodes);
429
+ for (let i = 0; i < current.length; i++) {
430
+ dispose(current[i]);
431
+ }
432
+ parent.replaceChildren(...next);
335
433
  }
336
434
 
337
435
  // src/reactivity/track-core.ts
@@ -492,6 +590,7 @@ function retrack(effectFn, subscriber) {
492
590
  }
493
591
  currentSubscriber = subscriber;
494
592
  const sub = subscriber;
593
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
495
594
  const epoch = ++subscriberEpochCounter;
496
595
  sub._epoch = epoch;
497
596
  sub._structDirty = false;
@@ -683,7 +782,12 @@ function drainQueue() {
683
782
  quarantined.add(sub);
684
783
  continue;
685
784
  }
686
- safeInvoke(sub);
785
+ const pushed = beginDisposerCapture(sub._cap ?? null);
786
+ try {
787
+ safeInvoke(sub);
788
+ } finally {
789
+ if (pushed) endDisposerCapture();
790
+ }
687
791
  }
688
792
  }
689
793
  function drainNotificationQueue() {
@@ -819,7 +923,7 @@ function forEachSubscriber(signal2, visit) {
819
923
  }
820
924
 
821
925
  // src/reactivity/track.ts
822
- var _runtimeVersion = true ? "4.5.0" : "dev";
926
+ var _runtimeVersion = true ? "4.6.0" : "dev";
823
927
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
824
928
  function resolveReactiveApi() {
825
929
  const g = globalThis;
@@ -1200,72 +1304,6 @@ function uniqueId(suffix) {
1200
1304
  return suffix ? `${id}-${suffix}` : id;
1201
1305
  }
1202
1306
 
1203
- // src/core/rendering/dispose.ts
1204
- var elementDisposers = /* @__PURE__ */ new WeakMap();
1205
- var MAX_DRAIN_TEARDOWNS = 1e4;
1206
- function reportDrainRunaway(label, executed, remaining) {
1207
- if (typeof console === "undefined") return;
1208
- console.error(
1209
- `[SibuJS ${label}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
1210
- { executed, remaining }
1211
- );
1212
- }
1213
- var activeBindingCount = 0;
1214
- function dispose(node) {
1215
- const stack = [node];
1216
- const order = [];
1217
- while (stack.length > 0) {
1218
- const current = stack.pop();
1219
- order.push(current);
1220
- const children = Array.from(current.childNodes);
1221
- for (let i = 0; i < children.length; i++) {
1222
- stack.push(children[i]);
1223
- }
1224
- }
1225
- for (let i = order.length - 1; i >= 0; i--) {
1226
- const current = order[i];
1227
- if (elementDisposers.has(current)) {
1228
- let executed = 0;
1229
- let runaway = false;
1230
- while (!runaway) {
1231
- const pending = elementDisposers.get(current);
1232
- if (!pending || pending.length === 0) break;
1233
- const snapshot = pending.slice();
1234
- elementDisposers.delete(current);
1235
- if (DEV) activeBindingCount -= snapshot.length;
1236
- for (let i2 = 0; i2 < snapshot.length; i2++) {
1237
- if (executed >= MAX_DRAIN_TEARDOWNS) {
1238
- const rest = snapshot.slice(i2);
1239
- const added = elementDisposers.get(current);
1240
- elementDisposers.set(current, added ? rest.concat(added) : rest);
1241
- if (DEV) activeBindingCount += rest.length;
1242
- reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1243
- runaway = true;
1244
- break;
1245
- }
1246
- executed++;
1247
- try {
1248
- snapshot[i2]();
1249
- } catch (err) {
1250
- reportError(err, { phase: "cleanup", name: "disposer" });
1251
- }
1252
- }
1253
- }
1254
- }
1255
- }
1256
- }
1257
- function replaceChildrenSafely(parent, ...next) {
1258
- for (let i = 0; i < next.length; i++) {
1259
- const node = next[i];
1260
- node.parentNode?.removeChild(node);
1261
- }
1262
- const current = Array.from(parent.childNodes);
1263
- for (let i = 0; i < current.length; i++) {
1264
- dispose(current[i]);
1265
- }
1266
- parent.replaceChildren(...next);
1267
- }
1268
-
1269
1307
  // src/utils/sanitize.ts
1270
1308
  function stripControlChars(value) {
1271
1309
  return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
@@ -1366,11 +1404,16 @@ function getDOMPool() {
1366
1404
  return _defaultPool;
1367
1405
  }
1368
1406
  var preloadedResources = /* @__PURE__ */ new Set();
1407
+ function hintKey(rel, as, crossorigin, href) {
1408
+ return `${rel}\0${as}\0${crossorigin}\0${href}`;
1409
+ }
1369
1410
  function preloadResource(url, type = "fetch") {
1370
1411
  const safe = sanitizeUrl(url);
1371
1412
  if (!safe) return;
1372
- if (preloadedResources.has(safe)) return;
1373
- preloadedResources.add(safe);
1413
+ const as = type === "script" || type === "style" || type === "image" ? type : "fetch";
1414
+ const key = hintKey("preload", as, as === "fetch" ? "anonymous" : "", safe);
1415
+ if (preloadedResources.has(key)) return;
1416
+ preloadedResources.add(key);
1374
1417
  const link = document.createElement("link");
1375
1418
  link.rel = "preload";
1376
1419
  link.href = safe;
@@ -1393,8 +1436,9 @@ function preloadResource(url, type = "fetch") {
1393
1436
  function prefetch(url) {
1394
1437
  const safe = sanitizeUrl(url);
1395
1438
  if (!safe) return;
1396
- if (preloadedResources.has(safe)) return;
1397
- preloadedResources.add(safe);
1439
+ const key = hintKey("prefetch", "", "", safe);
1440
+ if (preloadedResources.has(key)) return;
1441
+ preloadedResources.add(key);
1398
1442
  const link = document.createElement("link");
1399
1443
  link.rel = "prefetch";
1400
1444
  link.href = safe;
@@ -1463,65 +1507,92 @@ function block(factory) {
1463
1507
  }
1464
1508
 
1465
1509
  // src/performance/normalize.ts
1510
+ function dict(source) {
1511
+ const out = /* @__PURE__ */ Object.create(null);
1512
+ if (source) {
1513
+ for (const key of Object.keys(source)) defineOwn(out, key, source[key]);
1514
+ }
1515
+ return out;
1516
+ }
1517
+ function ownGet(obj, key) {
1518
+ return obj !== void 0 && Object.hasOwn(obj, key) ? obj[key] : void 0;
1519
+ }
1520
+ function defineOwn(obj, key, value) {
1521
+ Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
1522
+ }
1523
+ function readEntityId(entity, idKey, typeName) {
1524
+ const raw = entity !== null && typeof entity === "object" && Object.hasOwn(entity, idKey) ? entity[idKey] : void 0;
1525
+ if (typeof raw === "string") return raw;
1526
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
1527
+ throw new TypeError(
1528
+ `[normalize] "${typeName}" entity has no valid "${idKey}" (expected a string or finite number, got ${raw === null ? "null" : typeof raw})`
1529
+ );
1530
+ }
1466
1531
  function normalizedStore(schema) {
1467
1532
  const idKey = schema.idKey || "id";
1468
1533
  const [getState, setState] = signal({
1469
1534
  ids: [],
1470
- entities: {}
1535
+ entities: dict()
1471
1536
  });
1472
- const idOf = (entity) => String(entity[idKey]);
1537
+ const idOf = (entity) => readEntityId(entity, idKey, schema.name);
1473
1538
  function add(entity) {
1474
1539
  const id = idOf(entity);
1475
1540
  setState((prev) => {
1476
- const ids = prev.ids.includes(id) ? prev.ids : [...prev.ids, id];
1477
- return {
1478
- ids,
1479
- entities: { ...prev.entities, [id]: entity }
1480
- };
1541
+ const ids = Object.hasOwn(prev.entities, id) ? prev.ids : [...prev.ids, id];
1542
+ const entities = dict(prev.entities);
1543
+ defineOwn(entities, id, entity);
1544
+ return { ids, entities };
1481
1545
  });
1482
1546
  }
1483
1547
  function addMany(entities) {
1548
+ const ids = entities.map(idOf);
1484
1549
  setState((prev) => {
1485
1550
  const nextIds = [...prev.ids];
1486
- const nextEntities = { ...prev.entities };
1487
- for (const entity of entities) {
1488
- const id = idOf(entity);
1489
- if (!nextIds.includes(id)) {
1551
+ const nextEntities = dict(prev.entities);
1552
+ entities.forEach((entity, i) => {
1553
+ const id = ids[i];
1554
+ if (!Object.hasOwn(nextEntities, id)) {
1490
1555
  nextIds.push(id);
1491
1556
  }
1492
- nextEntities[id] = entity;
1493
- }
1557
+ defineOwn(nextEntities, id, entity);
1558
+ });
1494
1559
  return { ids: nextIds, entities: nextEntities };
1495
1560
  });
1496
1561
  }
1497
1562
  function get(id) {
1498
- const state2 = getState();
1499
- return state2.entities[id];
1563
+ return ownGet(getState().entities, id);
1500
1564
  }
1501
1565
  function getAll() {
1502
1566
  const state2 = getState();
1503
1567
  return state2.ids.map((id) => state2.entities[id]);
1504
1568
  }
1505
1569
  function update(id, partial) {
1506
- setState((prev) => {
1507
- const existing = prev.entities[id];
1508
- if (!existing) return prev;
1509
- return {
1510
- ids: prev.ids,
1511
- entities: {
1512
- ...prev.entities,
1513
- [id]: { ...existing, ...partial }
1514
- }
1515
- };
1570
+ const prev = getState();
1571
+ if (!Object.hasOwn(prev.entities, id)) return;
1572
+ if (partial !== null && typeof partial === "object" && Object.hasOwn(partial, idKey)) {
1573
+ const nextId = readEntityId(partial, idKey, schema.name);
1574
+ if (nextId !== id) {
1575
+ throw new Error(
1576
+ `[normalizedStore] update("${id}") cannot change "${idKey}" to "${nextId}"; remove and re-add the entity instead.`
1577
+ );
1578
+ }
1579
+ }
1580
+ setState((current) => {
1581
+ const existing = ownGet(current.entities, id);
1582
+ if (!existing) return current;
1583
+ const entities = dict(current.entities);
1584
+ defineOwn(entities, id, { ...existing, ...partial });
1585
+ return { ids: current.ids, entities };
1516
1586
  });
1517
1587
  }
1518
1588
  function remove(id) {
1519
1589
  setState((prev) => {
1520
- if (!prev.entities[id]) return prev;
1521
- const { [id]: _, ...remainingEntities } = prev.entities;
1590
+ if (!Object.hasOwn(prev.entities, id)) return prev;
1591
+ const entities = dict(prev.entities);
1592
+ delete entities[id];
1522
1593
  return {
1523
1594
  ids: prev.ids.filter((existingId) => existingId !== id),
1524
- entities: remainingEntities
1595
+ entities
1525
1596
  };
1526
1597
  });
1527
1598
  }
@@ -1532,31 +1603,37 @@ function normalizedStore(schema) {
1532
1603
  return { add, addMany, get, getAll, update, remove, select, getState };
1533
1604
  }
1534
1605
  function normalize(data, schema) {
1535
- const entities = {};
1606
+ const entities = dict();
1536
1607
  function ensureTable(name) {
1537
- if (!entities[name]) {
1538
- entities[name] = {};
1608
+ let table = ownGet(entities, name);
1609
+ if (!table) {
1610
+ table = dict();
1611
+ defineOwn(entities, name, table);
1539
1612
  }
1540
- return entities[name];
1613
+ return table;
1541
1614
  }
1542
1615
  function normalizeEntity(entity, entitySchema) {
1543
1616
  const entityIdKey = entitySchema.idKey || "id";
1544
- const id = String(entity[entityIdKey]);
1617
+ const id = readEntityId(entity, entityIdKey, entitySchema.name);
1545
1618
  const table = ensureTable(entitySchema.name);
1546
1619
  const flat = { ...entity };
1547
1620
  if (entitySchema.relations) {
1548
1621
  for (const [field, relationType] of Object.entries(entitySchema.relations)) {
1549
- const value = entity[field];
1622
+ const value = Object.hasOwn(entity, field) ? entity[field] : void 0;
1550
1623
  if (value == null) continue;
1551
1624
  const relSchema = { name: relationType };
1552
1625
  if (Array.isArray(value)) {
1553
- flat[field] = value.map((item) => normalizeEntity(item, relSchema));
1626
+ defineOwn(
1627
+ flat,
1628
+ field,
1629
+ value.map((item) => normalizeEntity(item, relSchema))
1630
+ );
1554
1631
  } else if (typeof value === "object") {
1555
- flat[field] = normalizeEntity(value, relSchema);
1632
+ defineOwn(flat, field, normalizeEntity(value, relSchema));
1556
1633
  }
1557
1634
  }
1558
1635
  }
1559
- table[id] = flat;
1636
+ defineOwn(table, id, flat);
1560
1637
  return id;
1561
1638
  }
1562
1639
  if (Array.isArray(data)) {
@@ -1567,20 +1644,24 @@ function normalize(data, schema) {
1567
1644
  return { result, entities };
1568
1645
  }
1569
1646
  function denormalize(id, entities, schema) {
1570
- const table = entities[schema.name];
1647
+ const table = ownGet(entities, schema.name);
1571
1648
  if (!table) return void 0;
1572
- const entity = table[id];
1649
+ const entity = ownGet(table, id);
1573
1650
  if (!entity) return void 0;
1574
1651
  const result = { ...entity };
1575
1652
  if (schema.relations) {
1576
1653
  for (const [field, relationType] of Object.entries(schema.relations)) {
1577
- const value = entity[field];
1654
+ const value = ownGet(entity, field);
1578
1655
  if (value == null) continue;
1579
1656
  const relSchema = { name: relationType };
1580
1657
  if (Array.isArray(value)) {
1581
- result[field] = value.map((relId) => denormalize(relId, entities, relSchema));
1658
+ defineOwn(
1659
+ result,
1660
+ field,
1661
+ value.map((relId) => denormalize(relId, entities, relSchema))
1662
+ );
1582
1663
  } else if (typeof value === "string") {
1583
- result[field] = denormalize(value, entities, relSchema);
1664
+ defineOwn(result, field, denormalize(value, entities, relSchema));
1584
1665
  }
1585
1666
  }
1586
1667
  }
@@ -1790,13 +1871,24 @@ function lazyChunk(id, loader, registry, fallback) {
1790
1871
  if (fallback) {
1791
1872
  container.appendChild(fallback());
1792
1873
  }
1874
+ let disposed = false;
1875
+ registerDisposer(container, () => {
1876
+ disposed = true;
1877
+ });
1793
1878
  registry.load(id, async () => {
1794
1879
  const mod = await loader();
1795
1880
  return typeof mod === "function" ? mod : mod.default;
1796
1881
  }).then((component) => {
1882
+ if (disposed) return;
1883
+ const node = component();
1884
+ if (disposed) {
1885
+ dispose(node);
1886
+ return;
1887
+ }
1797
1888
  clearChildren(container);
1798
- container.appendChild(component());
1889
+ container.appendChild(node);
1799
1890
  }).catch((err) => {
1891
+ if (disposed) return;
1800
1892
  clearChildren(container);
1801
1893
  const errorEl = document.createElement("div");
1802
1894
  errorEl.textContent = `Failed to load chunk '${id}': ${err.message}`;