sibujs 4.5.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +326 -133
  3. package/dist/browser.d.cts +46 -14
  4. package/dist/browser.d.ts +46 -14
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +2094 -1033
  7. package/dist/build.d.cts +133 -44
  8. package/dist/build.d.ts +133 -44
  9. package/dist/build.js +1372 -574
  10. package/dist/cdn.dev.global.js +10 -10
  11. package/dist/cdn.full.dev.global.js +11 -11
  12. package/dist/cdn.full.global.js +10 -10
  13. package/dist/cdn.global.js +10 -10
  14. package/dist/{chunk-2DCGACUU.js → chunk-2BPG2XDA.js} +251 -66
  15. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  16. package/dist/{chunk-4PMLNECI.js → chunk-3QWBSL5R.js} +98 -29
  17. package/dist/{chunk-KKLW7YWL.js → chunk-4AWA2PVD.js} +275 -144
  18. package/dist/chunk-5HZXGZ6T.js +24 -0
  19. package/dist/{chunk-PCT43HW3.js → chunk-7LUJQAOJ.js} +1 -1
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/chunk-BC2SECJD.js +44 -0
  22. package/dist/{chunk-TBYTO6BS.js → chunk-BSY63EM6.js} +136 -80
  23. package/dist/{chunk-FTIR4QW2.js → chunk-DKXRACVN.js} +6 -8
  24. package/dist/{chunk-ONOHFDLG.js → chunk-FNJXNGYZ.js} +3 -3
  25. package/dist/{chunk-XZZOBQAY.js → chunk-FQRUXCEE.js} +6 -5
  26. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-UGRX3S57.js → chunk-JWKYU5GV.js} +22 -4
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-RBTPLM32.js → chunk-RUSSKG6G.js} +13 -12
  31. package/dist/{chunk-7LN645I6.js → chunk-SLM3IA34.js} +3 -3
  32. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  33. package/dist/{chunk-B3WHI2QA.js → chunk-UNWRJRKC.js} +55 -31
  34. package/dist/{chunk-KEISJXBU.js → chunk-UOL2ECCS.js} +44 -17
  35. package/dist/{chunk-RJE2BNI4.js → chunk-VZSG24LS.js} +108 -45
  36. package/dist/{chunk-RIXRAYIU.js → chunk-WEQ3DMVL.js} +10 -4
  37. package/dist/{chunk-3JZ4L5TJ.js → chunk-WOLJZUFQ.js} +354 -90
  38. package/dist/{chunk-S373NSMK.js → chunk-XYV3EDB7.js} +283 -123
  39. package/dist/{chunk-VZKNK2V7.js → chunk-XZR4PXRE.js} +296 -38
  40. package/dist/{chunk-GW3SCCZG.js → chunk-Z3OHK6QT.js} +153 -110
  41. package/dist/{chunk-OMJJM3KM.js → chunk-ZVL7TY4K.js} +287 -130
  42. package/dist/{contracts-DBdg9J_a.d.ts → contracts-CLqzJnOV.d.ts} +36 -17
  43. package/dist/{contracts-DBdg9J_a.d.cts → contracts-CTOJXu-x.d.cts} +36 -17
  44. package/dist/{customElement-OB9CIsc5.d.cts → customElement-MmInOW1U.d.cts} +21 -0
  45. package/dist/{customElement-OB9CIsc5.d.ts → customElement-MmInOW1U.d.ts} +21 -0
  46. package/dist/data.cjs +410 -155
  47. package/dist/data.d.cts +166 -12
  48. package/dist/data.d.ts +166 -12
  49. package/dist/data.js +12 -9
  50. package/dist/devtools.cjs +98 -61
  51. package/dist/devtools.js +7 -8
  52. package/dist/dispose-GEIG2KOF.js +28 -0
  53. package/dist/ecosystem.cjs +372 -126
  54. package/dist/ecosystem.d.cts +20 -3
  55. package/dist/ecosystem.d.ts +20 -3
  56. package/dist/ecosystem.js +12 -12
  57. package/dist/extras.cjs +2366 -909
  58. package/dist/extras.d.cts +11 -9
  59. package/dist/extras.d.ts +11 -9
  60. package/dist/extras.js +39 -28
  61. package/dist/index.cjs +421 -158
  62. package/dist/index.d.cts +213 -171
  63. package/dist/index.d.ts +213 -171
  64. package/dist/index.js +24 -27
  65. package/dist/motion.cjs +118 -44
  66. package/dist/motion.js +5 -5
  67. package/dist/patterns.cjs +344 -53
  68. package/dist/patterns.d.cts +28 -9
  69. package/dist/patterns.d.ts +28 -9
  70. package/dist/patterns.js +8 -8
  71. package/dist/performance.cjs +325 -220
  72. package/dist/performance.d.cts +2 -2
  73. package/dist/performance.d.ts +2 -2
  74. package/dist/performance.js +8 -9
  75. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  76. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  77. package/dist/plugins.cjs +664 -233
  78. package/dist/plugins.d.cts +127 -14
  79. package/dist/plugins.d.ts +127 -14
  80. package/dist/plugins.js +96 -42
  81. package/dist/signal-EotCj4hS.d.cts +110 -0
  82. package/dist/signal-EotCj4hS.d.ts +110 -0
  83. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  84. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  85. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  86. package/dist/ssr.cjs +229 -84
  87. package/dist/ssr.d.cts +9 -3
  88. package/dist/ssr.d.ts +9 -3
  89. package/dist/ssr.js +11 -12
  90. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  91. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  92. package/dist/tagFactory-8qL9LCIx.d.cts +156 -0
  93. package/dist/tagFactory-BL2fymez.d.ts +156 -0
  94. package/dist/testing.cjs +2503 -2191
  95. package/dist/testing.d.cts +56 -5
  96. package/dist/testing.d.ts +56 -5
  97. package/dist/testing.js +580 -307
  98. package/dist/types-CJFViL6Q.d.cts +26 -0
  99. package/dist/types-CJFViL6Q.d.ts +26 -0
  100. package/dist/ui.cjs +732 -329
  101. package/dist/ui.d.cts +41 -7
  102. package/dist/ui.d.ts +41 -7
  103. package/dist/ui.js +151 -56
  104. package/dist/widgets.cjs +267 -291
  105. package/dist/widgets.js +9 -10
  106. package/package.json +4 -2
  107. package/dist/chunk-2WLZ6757.js +0 -149
  108. package/dist/chunk-CCSJMTRN.js +0 -15
  109. package/dist/chunk-QKRPLZ2V.js +0 -108
  110. package/dist/chunk-VUF4ALSW.js +0 -60
  111. package/dist/chunk-WWV3SJ3L.js +0 -131
  112. package/dist/dispose-46BOMMQJ.js +0 -19
  113. package/dist/plugin-D30wlGW5.d.cts +0 -71
  114. package/dist/plugin-D30wlGW5.d.ts +0 -71
  115. package/dist/tagFactory-DVoDpHye.d.cts +0 -215
  116. package/dist/tagFactory-DVoDpHye.d.ts +0 -215
@@ -1,39 +1,54 @@
1
1
  import {
2
2
  renderToString
3
- } from "./chunk-NIOYEGBQ.js";
3
+ } from "./chunk-HURREPU2.js";
4
+ import {
5
+ globalSingleton
6
+ } from "./chunk-7ZHH77QA.js";
7
+ import {
8
+ reportError
9
+ } from "./chunk-NYNYSPK7.js";
4
10
 
5
11
  // src/plugins/modular.ts
6
12
  function createModuleRegistry() {
7
13
  const modules = /* @__PURE__ */ new Map();
8
- function resolveInternal(name, stack) {
14
+ const resolving = [];
15
+ function resolveInternal(name) {
9
16
  const entry = modules.get(name);
10
17
  if (!entry) {
11
18
  throw new Error(`[ModuleRegistry] Module "${name}" is not registered.`);
12
19
  }
13
- if (entry.loaded) {
20
+ if (entry.state === "loaded") {
14
21
  return entry.value;
15
22
  }
16
- if (stack.has(name)) {
17
- const cycle = [...stack, name].join(" -> ");
23
+ if (entry.state === "resolving") {
24
+ const cycle = [...resolving.slice(resolving.indexOf(name)), name].join(" -> ");
18
25
  throw new Error(`[ModuleRegistry] Circular dependency detected: ${cycle}`);
19
26
  }
20
- stack.add(name);
21
- for (const dep of entry.deps) {
22
- resolveInternal(dep, stack);
27
+ entry.state = "resolving";
28
+ resolving.push(name);
29
+ try {
30
+ for (const dep of entry.deps) {
31
+ resolveInternal(dep);
32
+ }
33
+ entry.value = entry.factory();
34
+ entry.state = "loaded";
35
+ return entry.value;
36
+ } catch (err) {
37
+ entry.state = "unloaded";
38
+ entry.value = void 0;
39
+ throw err;
40
+ } finally {
41
+ resolving.pop();
23
42
  }
24
- stack.delete(name);
25
- entry.value = entry.factory();
26
- entry.loaded = true;
27
- return entry.value;
28
43
  }
29
44
  return {
30
45
  /** Register a module with its factory function and optional dependencies */
31
46
  register(name, factory, deps = []) {
32
- modules.set(name, { factory, deps, loaded: false });
47
+ modules.set(name, { factory, deps, state: "unloaded" });
33
48
  },
34
49
  /** Resolve a module, loading its dependencies first */
35
50
  resolve(name) {
36
- return resolveInternal(name, /* @__PURE__ */ new Set());
51
+ return resolveInternal(name);
37
52
  },
38
53
  /** Check if a module is registered */
39
54
  has(name) {
@@ -68,7 +83,7 @@ function createModuleRegistry() {
68
83
  /** Reset all loaded modules back to unloaded state (useful for testing) */
69
84
  reset() {
70
85
  for (const entry of modules.values()) {
71
- entry.loaded = false;
86
+ entry.state = "unloaded";
72
87
  entry.value = void 0;
73
88
  }
74
89
  }
@@ -96,46 +111,83 @@ function createBundle(modules) {
96
111
  function lazyModule(loader) {
97
112
  let cached;
98
113
  let loadedFlag = false;
99
- const handle = {
114
+ let inFlight = null;
115
+ return {
100
116
  get loaded() {
101
117
  return loadedFlag;
102
118
  },
103
- async get() {
104
- if (loadedFlag) return cached;
105
- cached = await loader();
106
- loadedFlag = true;
107
- return cached;
119
+ get() {
120
+ if (loadedFlag) return Promise.resolve(cached);
121
+ if (inFlight) return inFlight;
122
+ let started;
123
+ try {
124
+ started = Promise.resolve(loader());
125
+ } catch (err) {
126
+ started = Promise.reject(err);
127
+ }
128
+ const attempt = started.then(
129
+ (value) => {
130
+ cached = value;
131
+ loadedFlag = true;
132
+ if (inFlight === attempt) inFlight = null;
133
+ return value;
134
+ },
135
+ (err) => {
136
+ if (inFlight === attempt) inFlight = null;
137
+ throw err;
138
+ }
139
+ );
140
+ inFlight = attempt;
141
+ return attempt;
108
142
  }
109
143
  };
110
- return handle;
111
144
  }
145
+ var MODULE_ENTRY_POINTS = {
146
+ main: "./index.ts",
147
+ data: "./data.ts",
148
+ browser: "./browser.ts",
149
+ patterns: "./patterns.ts",
150
+ motion: "./motion.ts",
151
+ ui: "./ui.ts",
152
+ widgets: "./widgets.ts",
153
+ ssr: "./ssr.ts",
154
+ devtools: "./devtools.ts",
155
+ performance: "./performance.ts",
156
+ ecosystem: "./ecosystem.ts",
157
+ plugins: "./plugins.ts",
158
+ build: "./build.ts",
159
+ testing: "./testing.ts",
160
+ extras: "./extras.ts"
161
+ };
162
+ var CDN_ENTRY_POINTS = {
163
+ cdn: "./dist/cdn.global.js",
164
+ "cdn-dev": "./dist/cdn.dev.global.js",
165
+ "cdn-full": "./dist/cdn.full.global.js",
166
+ "cdn-full-dev": "./dist/cdn.full.dev.global.js"
167
+ };
112
168
  var packageInfo = {
113
- name: "sibu",
114
- version: "1.0.0",
115
- entryPoints: {
116
- main: "./index.ts",
117
- core: "./src/core/html.ts",
118
- hooks: "./src/core/signals/signal.ts",
119
- router: "./src/plugins/router.ts",
120
- i18n: "./src/plugins/i18n.ts",
121
- testing: "./src/testing/index.ts",
122
- ssr: "./src/core/ssr.ts"
123
- },
169
+ name: "sibujs",
170
+ version: true ? "4.7.0" : "dev",
171
+ entryPoints: MODULE_ENTRY_POINTS,
124
172
  /**
125
- * Generate a package.json `exports` map for Node.js subpath exports.
126
- * Maps each entry point to its import, require, and types paths.
173
+ * Generate the package.json `exports` map. Module entries resolve to the
174
+ * `.js` (ESM), `.cjs` and `.d.ts` files tsup emits into `dist/`; CDN entries
175
+ * resolve to their prebuilt global script.
127
176
  */
128
177
  generateExportsMap() {
129
178
  const exportsMap = {};
130
- for (const [name, sourcePath] of Object.entries(this.entryPoints)) {
179
+ for (const [name, sourcePath] of Object.entries(MODULE_ENTRY_POINTS)) {
131
180
  const distPath = sourcePath.replace(/^\.\//, "./dist/").replace(/\.ts$/, "");
132
181
  const subpath = name === "main" ? "." : `./${name}`;
133
182
  exportsMap[subpath] = {
134
- import: `${distPath}.mjs`,
135
- require: `${distPath}.cjs`,
136
- types: `${distPath}.d.ts`
183
+ types: `${distPath}.d.ts`,
184
+ import: `${distPath}.js`,
185
+ require: `${distPath}.cjs`
137
186
  };
138
187
  }
188
+ for (const [name, file] of Object.entries(CDN_ENTRY_POINTS)) {
189
+ exportsMap[`./${name}`] = { default: file };
190
+ }
139
191
  return exportsMap;
140
192
  }
141
193
  };
@@ -260,29 +312,26 @@ var env = {
260
312
  };
261
313
 
262
314
  // src/plugins/versioning.ts
263
- var VERSION = "1.0.0";
315
+ var VERSION = true ? "4.7.0" : "dev";
316
+ var NUM = "(?:0|[1-9]\\d*)";
317
+ var PRE_ID = `(?:${NUM}|\\d*[a-zA-Z-][0-9a-zA-Z-]*)`;
318
+ var BUILD_ID = "[0-9a-zA-Z-]+";
319
+ var SEMVER_PATTERN = new RegExp(
320
+ `^(${NUM})(?:\\.(${NUM})(?:\\.(${NUM})(?:-(${PRE_ID}(?:\\.${PRE_ID})*))?(?:\\+(${BUILD_ID}(?:\\.${BUILD_ID})*))?)?)?$`
321
+ );
264
322
  function parseSemVer(version) {
265
- const trimmed = version.trim().replace(/^v/i, "");
266
- const prereleaseIndex = trimmed.indexOf("-");
267
- let main;
268
- let prerelease;
269
- if (prereleaseIndex !== -1) {
270
- main = trimmed.slice(0, prereleaseIndex);
271
- prerelease = trimmed.slice(prereleaseIndex + 1);
272
- } else {
273
- main = trimmed;
274
- }
275
- const parts = main.split(".");
276
- const major = parseInt(parts[0], 10);
277
- const minor = parseInt(parts[1] || "0", 10);
278
- const patch = parseInt(parts[2] || "0", 10);
279
- if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
323
+ const trimmed = String(version).trim().replace(/^v/i, "");
324
+ const match = SEMVER_PATTERN.exec(trimmed);
325
+ if (!match) {
280
326
  throw new Error(`[Versioning] Invalid semver string: "${version}"`);
281
327
  }
282
- const result = { major, minor, patch };
283
- if (prerelease !== void 0) {
284
- result.prerelease = prerelease;
285
- }
328
+ const result = {
329
+ major: Number(match[1]),
330
+ minor: match[2] === void 0 ? 0 : Number(match[2]),
331
+ patch: match[3] === void 0 ? 0 : Number(match[3])
332
+ };
333
+ if (match[4] !== void 0) result.prerelease = match[4];
334
+ if (match[5] !== void 0) result.build = match[5];
286
335
  return result;
287
336
  }
288
337
  function compareSemVer(a, b) {
@@ -365,82 +414,154 @@ function satisfies(version, range) {
365
414
  const target = parseSemVer(exactStr);
366
415
  return compareSemVer(v, target) === 0;
367
416
  }
417
+ var MigrationStorageError = class extends Error {
418
+ constructor(version, cause) {
419
+ const reason = cause instanceof Error ? cause.message : String(cause);
420
+ super(`[Versioning] Migration ${version} completed, but recording it in storage failed: ${reason}`);
421
+ this.name = "MigrationStorageError";
422
+ this.version = version;
423
+ this.cause = cause;
424
+ }
425
+ };
426
+ var _migrationQueues = globalSingleton(
427
+ /* @__PURE__ */ Symbol.for("sibujs.migrationQueues.v1"),
428
+ () => /* @__PURE__ */ new WeakMap()
429
+ );
430
+ var NO_STORAGE = {};
368
431
  function createMigrationRunner(config) {
369
432
  const storageKey = config.storageKey || "__sibu_migration_version__";
370
433
  const sortedMigrations = [...config.migrations].sort((a, b) => compareSemVer(a.version, b.version));
371
434
  function getStorage() {
435
+ if (config.storage) return config.storage;
372
436
  try {
373
437
  return typeof localStorage !== "undefined" ? localStorage : null;
374
438
  } catch {
375
439
  return null;
376
440
  }
377
441
  }
442
+ function getAppliedVersion() {
443
+ const storage = getStorage();
444
+ if (!storage) return null;
445
+ return storage.getItem(storageKey);
446
+ }
447
+ function getPending() {
448
+ const appliedVersion = getAppliedVersion();
449
+ if (!appliedVersion) {
450
+ return sortedMigrations.filter((m) => compareSemVer(m.version, config.currentVersion) <= 0);
451
+ }
452
+ return sortedMigrations.filter(
453
+ (m) => compareSemVer(m.version, appliedVersion) > 0 && compareSemVer(m.version, config.currentVersion) <= 0
454
+ );
455
+ }
456
+ function writeApplied(version, migrationVersion) {
457
+ const storage = getStorage();
458
+ if (!storage) return;
459
+ try {
460
+ if (version === null) storage.removeItem(storageKey);
461
+ else storage.setItem(storageKey, version);
462
+ } catch (e) {
463
+ throw new MigrationStorageError(migrationVersion, e);
464
+ }
465
+ }
466
+ function serialize(operation) {
467
+ const identity = getStorage() ?? NO_STORAGE;
468
+ let queues = _migrationQueues.get(identity);
469
+ if (!queues) {
470
+ queues = /* @__PURE__ */ new Map();
471
+ _migrationQueues.set(identity, queues);
472
+ }
473
+ const run = (queues.get(storageKey) ?? Promise.resolve()).then(operation, operation);
474
+ const tail = run.then(
475
+ () => void 0,
476
+ () => void 0
477
+ );
478
+ queues.set(storageKey, tail);
479
+ const settled = queues;
480
+ tail.then(() => {
481
+ if (settled.get(storageKey) === tail) settled.delete(storageKey);
482
+ });
483
+ return run;
484
+ }
378
485
  return {
379
486
  /** Get the last applied migration version from storage */
380
- getAppliedVersion() {
381
- const storage = getStorage();
382
- if (!storage) return null;
383
- return storage.getItem(storageKey);
384
- },
487
+ getAppliedVersion,
385
488
  /** Get pending migrations that haven't been applied */
386
- getPending() {
387
- const appliedVersion = this.getAppliedVersion();
388
- if (!appliedVersion) {
389
- return sortedMigrations.filter((m) => compareSemVer(m.version, config.currentVersion) <= 0);
390
- }
391
- return sortedMigrations.filter(
392
- (m) => compareSemVer(m.version, appliedVersion) > 0 && compareSemVer(m.version, config.currentVersion) <= 0
393
- );
394
- },
395
- /** Run all pending migrations in order */
396
- async migrate() {
397
- const pending = this.getPending();
398
- const applied = [];
399
- const errors = [];
400
- for (const migration of pending) {
489
+ getPending,
490
+ /**
491
+ * Run all pending migrations in order. Concurrent calls (and calls racing
492
+ * `rollback()`) run one after another, each recomputing what is pending —
493
+ * on this runner and on every other runner in this realm sharing its
494
+ * storage and storage key.
495
+ */
496
+ migrate() {
497
+ return serialize(async () => {
498
+ const applied = [];
499
+ const errors = [];
500
+ let pending;
401
501
  try {
402
- await migration.up();
403
- applied.push(migration.version);
404
- const storage = getStorage();
405
- if (storage) {
406
- storage.setItem(storageKey, migration.version);
407
- }
502
+ pending = getPending();
408
503
  } catch (e) {
409
504
  errors.push({
410
- version: migration.version,
505
+ version: getAppliedVersion() ?? "",
411
506
  error: e instanceof Error ? e : new Error(String(e))
412
507
  });
413
- break;
508
+ return { applied, errors };
414
509
  }
415
- }
416
- return { applied, errors };
510
+ for (const migration of pending) {
511
+ try {
512
+ await migration.up();
513
+ } catch (e) {
514
+ errors.push({
515
+ version: migration.version,
516
+ error: e instanceof Error ? e : new Error(String(e))
517
+ });
518
+ break;
519
+ }
520
+ applied.push(migration.version);
521
+ try {
522
+ writeApplied(migration.version, migration.version);
523
+ } catch (e) {
524
+ errors.push({ version: migration.version, error: e });
525
+ break;
526
+ }
527
+ }
528
+ return { applied, errors };
529
+ });
417
530
  },
418
- /** Rollback to a specific version */
419
- async rollback(targetVersion) {
420
- const appliedVersion = this.getAppliedVersion();
421
- const rolledBack = [];
422
- if (!appliedVersion) {
423
- return { rolledBack };
424
- }
425
- const toRollback = sortedMigrations.filter((m) => compareSemVer(m.version, targetVersion) > 0 && compareSemVer(m.version, appliedVersion) <= 0).reverse();
426
- for (const migration of toRollback) {
427
- if (!migration.down) {
428
- throw new Error(
429
- `[Versioning] Migration ${migration.version} ("${migration.description}") does not have a down() function and cannot be rolled back.`
430
- );
531
+ /**
532
+ * Rollback to a specific version.
533
+ *
534
+ * The applied version is checkpointed after EVERY successful `down()`, so a
535
+ * rollback that fails part-way leaves storage describing what is actually
536
+ * still applied, and a retry does not repeat completed `down()` steps.
537
+ * Throws the failing `down()`'s error, a missing-`down()` error, or a
538
+ * {@link MigrationStorageError} if a checkpoint cannot be written.
539
+ */
540
+ rollback(targetVersion) {
541
+ return serialize(async () => {
542
+ const appliedVersion = getAppliedVersion();
543
+ const rolledBack = [];
544
+ if (!appliedVersion) {
545
+ return { rolledBack };
431
546
  }
432
- await migration.down();
433
- rolledBack.push(migration.version);
434
- }
435
- const storage = getStorage();
436
- if (storage) {
437
- if (targetVersion === "0.0.0") {
438
- storage.removeItem(storageKey);
439
- } else {
440
- storage.setItem(storageKey, targetVersion);
547
+ const toRollback = sortedMigrations.filter((m) => compareSemVer(m.version, targetVersion) > 0 && compareSemVer(m.version, appliedVersion) <= 0).reverse();
548
+ for (const migration of toRollback) {
549
+ if (!migration.down) {
550
+ throw new Error(
551
+ `[Versioning] Migration ${migration.version} ("${migration.description}") does not have a down() function and cannot be rolled back.`
552
+ );
553
+ }
441
554
  }
442
- }
443
- return { rolledBack };
555
+ for (const migration of toRollback) {
556
+ await migration.down();
557
+ rolledBack.push(migration.version);
558
+ const index = sortedMigrations.indexOf(migration);
559
+ const previous = index > 0 ? sortedMigrations[index - 1].version : null;
560
+ writeApplied(previous, migration.version);
561
+ }
562
+ writeApplied(targetVersion === "0.0.0" ? null : targetVersion, targetVersion);
563
+ return { rolledBack };
564
+ });
444
565
  }
445
566
  };
446
567
  }
@@ -490,28 +611,43 @@ function preloadCritical(resources) {
490
611
  document.head.appendChild(link);
491
612
  }
492
613
  }
493
- function prerenderRoutes(routes, options) {
494
- const maxCacheSize = options?.maxCacheSize ?? 50;
495
- const cacheTTL = options?.cacheTTL ?? 0;
496
- const cache = /* @__PURE__ */ new Map();
497
- function isValid(entry) {
498
- if (cacheTTL === 0) return true;
499
- return Date.now() - entry.timestamp < cacheTTL;
614
+ function assertCacheSize(name, value) {
615
+ if (!Number.isInteger(value) || value < 0) {
616
+ throw new RangeError(`[Startup] ${name} must be a non-negative integer, got ${value}`);
617
+ }
618
+ }
619
+ function makeRoomFor(cache, key, maxSize, isValid) {
620
+ if (maxSize === 0) return false;
621
+ if (cache.has(key)) return true;
622
+ if (cache.size < maxSize) return true;
623
+ for (const [k, entry] of cache) {
624
+ if (!isValid(entry)) cache.delete(k);
500
625
  }
501
- function evictOldest() {
502
- if (cache.size < maxCacheSize) return;
626
+ while (cache.size >= maxSize) {
503
627
  let oldestKey = null;
504
628
  let oldestTime = Infinity;
505
- for (const [key, entry] of cache) {
629
+ for (const [k, entry] of cache) {
506
630
  if (entry.timestamp < oldestTime) {
507
631
  oldestTime = entry.timestamp;
508
- oldestKey = key;
632
+ oldestKey = k;
509
633
  }
510
634
  }
511
- if (oldestKey) cache.delete(oldestKey);
635
+ if (oldestKey === null) break;
636
+ cache.delete(oldestKey);
637
+ }
638
+ return true;
639
+ }
640
+ function prerenderRoutes(routes, options) {
641
+ const maxCacheSize = options?.maxCacheSize ?? 50;
642
+ const cacheTTL = options?.cacheTTL ?? 0;
643
+ assertCacheSize("maxCacheSize", maxCacheSize);
644
+ const cache = /* @__PURE__ */ new Map();
645
+ function isValid(entry) {
646
+ if (cacheTTL === 0) return true;
647
+ return Date.now() - entry.timestamp < cacheTTL;
512
648
  }
513
649
  for (const route of routes) {
514
- evictOldest();
650
+ if (!makeRoomFor(cache, route.path, maxCacheSize, isValid)) break;
515
651
  const html = renderToString(route.component());
516
652
  cache.set(route.path, { html, timestamp: Date.now() });
517
653
  }
@@ -563,6 +699,7 @@ function prerenderRoutes(routes, options) {
563
699
  function createSSRCache(config) {
564
700
  const maxSize = config?.maxSize ?? 100;
565
701
  const defaultTTL = config?.defaultTTL ?? 6e4;
702
+ assertCacheSize("maxSize", maxSize);
566
703
  const cache = /* @__PURE__ */ new Map();
567
704
  let hits = 0;
568
705
  let misses = 0;
@@ -570,18 +707,6 @@ function createSSRCache(config) {
570
707
  if (entry.ttl === 0) return true;
571
708
  return Date.now() - entry.timestamp < entry.ttl;
572
709
  }
573
- function evictOldest() {
574
- if (cache.size < maxSize) return;
575
- let oldestKey = null;
576
- let oldestTime = Infinity;
577
- for (const [key, entry] of cache) {
578
- if (entry.timestamp < oldestTime) {
579
- oldestTime = entry.timestamp;
580
- oldestKey = key;
581
- }
582
- }
583
- if (oldestKey) cache.delete(oldestKey);
584
- }
585
710
  return {
586
711
  /** Get cached HTML for a key, or undefined if not cached or expired */
587
712
  get(key) {
@@ -600,7 +725,7 @@ function createSSRCache(config) {
600
725
  },
601
726
  /** Cache an HTML string with an optional TTL override */
602
727
  set(key, html, ttl) {
603
- evictOldest();
728
+ if (!makeRoomFor(cache, key, maxSize, isValid)) return;
604
729
  cache.set(key, {
605
730
  html,
606
731
  timestamp: Date.now(),
@@ -633,25 +758,30 @@ function createSSRCache(config) {
633
758
  }
634
759
  };
635
760
  }
761
+ var DEFER_IDLE_TIMEOUT_MS = 2e3;
636
762
  function deferNonCritical(tasks) {
637
763
  if (tasks.length === 0) return;
638
- const schedule = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : (cb) => setTimeout(cb, 1);
764
+ const schedule = typeof requestIdleCallback !== "undefined" ? (cb) => {
765
+ requestIdleCallback(cb, { timeout: DEFER_IDLE_TIMEOUT_MS });
766
+ } : (cb) => {
767
+ setTimeout(cb, 1);
768
+ };
639
769
  const queue = [...tasks];
640
770
  let index = 0;
641
771
  function processNext(deadline) {
772
+ let ranOne = false;
642
773
  while (index < queue.length) {
643
- if (deadline && deadline.timeRemaining() < 1) {
774
+ if (ranOne && deadline && deadline.timeRemaining() < 1) {
644
775
  schedule(processNext);
645
776
  return;
646
777
  }
647
778
  try {
648
779
  queue[index]();
649
780
  } catch (e) {
650
- if (typeof console !== "undefined") {
651
- console.error("[Startup] Deferred task failed:", e);
652
- }
781
+ reportError(e, { phase: "scheduler", name: "deferNonCritical" });
653
782
  }
654
783
  index++;
784
+ ranOne = true;
655
785
  }
656
786
  }
657
787
  schedule(processNext);
@@ -714,6 +844,7 @@ export {
714
844
  parseSemVer,
715
845
  compareSemVer,
716
846
  satisfies,
847
+ MigrationStorageError,
717
848
  createMigrationRunner,
718
849
  checkCompatibility,
719
850
  preloadCritical,
@@ -0,0 +1,24 @@
1
+ // src/utils/adoptThenable.ts
2
+ function adoptThenable(value) {
3
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
4
+ let then;
5
+ try {
6
+ then = value.then;
7
+ } catch (error) {
8
+ return Promise.reject(error);
9
+ }
10
+ if (typeof then !== "function") return null;
11
+ return new Promise((resolve, reject) => {
12
+ queueMicrotask(() => {
13
+ try {
14
+ Reflect.apply(then, value, [resolve, reject]);
15
+ } catch (error) {
16
+ reject(error);
17
+ }
18
+ });
19
+ });
20
+ }
21
+
22
+ export {
23
+ adoptThenable
24
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SVG_NS,
3
3
  tagFactory
4
- } from "./chunk-KEISJXBU.js";
4
+ } from "./chunk-UOL2ECCS.js";
5
5
 
6
6
  // src/core/rendering/html.ts
7
7
  var html = tagFactory("html");
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __require,
3
3
  devWarn
4
- } from "./chunk-2WLZ6757.js";
4
+ } from "./chunk-NYNYSPK7.js";
5
5
 
6
6
  // src/core/ssr-context.ts
7
7
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
@@ -74,16 +74,18 @@ function disableSSR() {
74
74
  getSSRStore().ssr = false;
75
75
  }
76
76
  function runInSSRContext(fn) {
77
- const store = { ssr: true, suspenseIdCounter: 0, locale: void 0 };
77
+ const store = { ssr: true, suspenseIdCounter: 0, idCounter: 0, locale: void 0 };
78
78
  if (als) {
79
79
  return als.run(store, fn);
80
80
  }
81
81
  warnMissingAsyncLocalStorage();
82
82
  const prevSSR = fallbackStore.ssr;
83
83
  const prevCounter = fallbackStore.suspenseIdCounter;
84
+ const prevIdCounter = fallbackStore.idCounter;
84
85
  const prevLocale = fallbackStore.locale;
85
86
  fallbackStore.ssr = true;
86
87
  fallbackStore.suspenseIdCounter = 0;
88
+ fallbackStore.idCounter = 0;
87
89
  fallbackStore.locale = void 0;
88
90
  _shared.fallbackDepth++;
89
91
  try {
@@ -92,6 +94,7 @@ function runInSSRContext(fn) {
92
94
  _shared.fallbackDepth--;
93
95
  fallbackStore.ssr = prevSSR;
94
96
  fallbackStore.suspenseIdCounter = prevCounter;
97
+ fallbackStore.idCounter = prevIdCounter;
95
98
  fallbackStore.locale = prevLocale;
96
99
  }
97
100
  }