sibujs 1.4.0 → 2.0.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 (190) hide show
  1. package/README.md +105 -119
  2. package/dist/browser.cjs +288 -80
  3. package/dist/browser.d.cts +19 -9
  4. package/dist/browser.d.ts +19 -9
  5. package/dist/browser.js +6 -6
  6. package/dist/build.cjs +1019 -313
  7. package/dist/build.d.cts +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.js +15 -13
  10. package/dist/cdn.global.js +17 -16
  11. package/dist/chunk-2RA7SHDA.js +65 -0
  12. package/dist/chunk-2UPRY23K.js +80 -0
  13. package/dist/chunk-3JHCYHWN.js +125 -0
  14. package/dist/{chunk-ZWKZCBO6.js → chunk-3LR7GLWQ.js} +154 -33
  15. package/dist/{chunk-3AIRKM3B.js → chunk-3NSGB5JN.js} +115 -34
  16. package/dist/{chunk-3ARAQO7B.js → chunk-52YJLLRO.js} +29 -6
  17. package/dist/chunk-54EDRCEF.js +93 -0
  18. package/dist/chunk-7JDB7I65.js +1327 -0
  19. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  20. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  21. package/dist/{chunk-WR5D4EGH.js → chunk-GTBNNBJ6.js} +14 -2
  22. package/dist/chunk-HB24TBAF.js +121 -0
  23. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  24. package/dist/{chunk-JAKHTMQU.js → chunk-JA6667UN.js} +206 -46
  25. package/dist/{chunk-77L6NL3X.js → chunk-JXMMDLBY.js} +306 -183
  26. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  27. package/dist/{chunk-F3FA4F32.js → chunk-KLRMB5ZS.js} +135 -79
  28. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  29. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  30. package/dist/{chunk-TSOKIX5Z.js → chunk-MIUAXB7K.js} +126 -74
  31. package/dist/{chunk-QWZG56ET.js → chunk-ND2664SF.js} +558 -190
  32. package/dist/{chunk-JCI5M6U6.js → chunk-O2MNQFLP.js} +261 -79
  33. package/dist/{chunk-EWFVA3TJ.js → chunk-R73P76YZ.js} +1 -1
  34. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  35. package/dist/chunk-UCS6AMJ7.js +79 -0
  36. package/dist/{chunk-ZD6OAMTH.js → chunk-VLPPXTYG.js} +90 -35
  37. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  38. package/dist/{contracts-xo5ckdRP.d.cts → contracts-ey_Qh8ef.d.cts} +7 -8
  39. package/dist/{contracts-xo5ckdRP.d.ts → contracts-ey_Qh8ef.d.ts} +7 -8
  40. package/dist/{customElement-D2DJp_xn.d.cts → customElement-CPfIrbvg.d.cts} +18 -9
  41. package/dist/{customElement-D2DJp_xn.d.ts → customElement-CPfIrbvg.d.ts} +18 -9
  42. package/dist/data.cjs +452 -100
  43. package/dist/data.d.cts +20 -2
  44. package/dist/data.d.ts +20 -2
  45. package/dist/data.js +11 -9
  46. package/dist/devtools.cjs +535 -247
  47. package/dist/devtools.d.cts +1 -1
  48. package/dist/devtools.d.ts +1 -1
  49. package/dist/devtools.js +34 -30
  50. package/dist/ecosystem.cjs +499 -143
  51. package/dist/ecosystem.d.cts +13 -11
  52. package/dist/ecosystem.d.ts +13 -11
  53. package/dist/ecosystem.js +12 -11
  54. package/dist/extras.cjs +3639 -1629
  55. package/dist/extras.d.cts +11 -11
  56. package/dist/extras.d.ts +11 -11
  57. package/dist/extras.js +58 -45
  58. package/dist/index.cjs +1023 -313
  59. package/dist/index.d.cts +128 -55
  60. package/dist/index.d.ts +128 -55
  61. package/dist/index.js +28 -16
  62. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  63. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  64. package/dist/motion.cjs +90 -36
  65. package/dist/motion.d.cts +1 -1
  66. package/dist/motion.d.ts +1 -1
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +414 -81
  69. package/dist/patterns.d.cts +53 -20
  70. package/dist/patterns.d.ts +53 -20
  71. package/dist/patterns.js +7 -7
  72. package/dist/performance.cjs +364 -108
  73. package/dist/performance.d.cts +29 -17
  74. package/dist/performance.d.ts +29 -17
  75. package/dist/performance.js +13 -6
  76. package/dist/plugin-D30wlGW5.d.cts +71 -0
  77. package/dist/plugin-D30wlGW5.d.ts +71 -0
  78. package/dist/plugins.cjs +652 -271
  79. package/dist/plugins.d.cts +13 -6
  80. package/dist/plugins.d.ts +13 -6
  81. package/dist/plugins.js +116 -50
  82. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  83. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  84. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  85. package/dist/ssr.cjs +648 -219
  86. package/dist/ssr.d.cts +27 -7
  87. package/dist/ssr.d.ts +27 -7
  88. package/dist/ssr.js +12 -11
  89. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.cts} +9 -1
  90. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.ts} +9 -1
  91. package/dist/testing.cjs +252 -63
  92. package/dist/testing.d.cts +17 -4
  93. package/dist/testing.d.ts +17 -4
  94. package/dist/testing.js +100 -44
  95. package/dist/ui.cjs +576 -168
  96. package/dist/ui.d.cts +13 -16
  97. package/dist/ui.d.ts +13 -16
  98. package/dist/ui.js +20 -17
  99. package/dist/widgets.cjs +1001 -93
  100. package/dist/widgets.d.cts +104 -2
  101. package/dist/widgets.d.ts +104 -2
  102. package/dist/widgets.js +9 -7
  103. package/package.json +8 -2
  104. package/dist/chunk-32DY64NT.js +0 -282
  105. package/dist/chunk-3CRQALYP.js +0 -877
  106. package/dist/chunk-4EI4AG32.js +0 -482
  107. package/dist/chunk-4MYMUBRS.js +0 -21
  108. package/dist/chunk-6HLLIF3K.js +0 -398
  109. package/dist/chunk-6LSNVCS2.js +0 -937
  110. package/dist/chunk-6SA3QQES.js +0 -61
  111. package/dist/chunk-7BF6TK55.js +0 -1097
  112. package/dist/chunk-7TQKR4PP.js +0 -294
  113. package/dist/chunk-7V26P53V.js +0 -712
  114. package/dist/chunk-AZ3ISID5.js +0 -298
  115. package/dist/chunk-B7SWRFUT.js +0 -332
  116. package/dist/chunk-BGN5ZMP4.js +0 -26
  117. package/dist/chunk-BTU3TJDS.js +0 -365
  118. package/dist/chunk-BW3WT46K.js +0 -937
  119. package/dist/chunk-C6KFWOFV.js +0 -616
  120. package/dist/chunk-CHF5OHIA.js +0 -61
  121. package/dist/chunk-CHJ27IGK.js +0 -26
  122. package/dist/chunk-CMBFNA7L.js +0 -27
  123. package/dist/chunk-DAHRH4ON.js +0 -331
  124. package/dist/chunk-DKOHBI74.js +0 -924
  125. package/dist/chunk-DTCOOBMX.js +0 -725
  126. package/dist/chunk-EBGIRKQY.js +0 -616
  127. package/dist/chunk-EUZND3CB.js +0 -27
  128. package/dist/chunk-EVCZO745.js +0 -365
  129. package/dist/chunk-FGOEVHY3.js +0 -60
  130. package/dist/chunk-G3BOQPVO.js +0 -365
  131. package/dist/chunk-GCOK2LC3.js +0 -282
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-K5ZUMYVS.js +0 -89
  134. package/dist/chunk-KQPDEVVS.js +0 -398
  135. package/dist/chunk-L6JRBDNS.js +0 -60
  136. package/dist/chunk-LA6KQEDU.js +0 -712
  137. package/dist/chunk-MDVXJWFN.js +0 -304
  138. package/dist/chunk-MEZVEBPN.js +0 -2008
  139. package/dist/chunk-MK4ERFYL.js +0 -2249
  140. package/dist/chunk-MLKGABMK.js +0 -9
  141. package/dist/chunk-MQ5GOYPH.js +0 -2249
  142. package/dist/chunk-N6IZB6KJ.js +0 -567
  143. package/dist/chunk-NEKUBFPT.js +0 -60
  144. package/dist/chunk-NHUC2QWH.js +0 -282
  145. package/dist/chunk-NMRUZALC.js +0 -1097
  146. package/dist/chunk-NYVAC6P5.js +0 -37
  147. package/dist/chunk-OF7UZIVB.js +0 -725
  148. package/dist/chunk-P6W3STU4.js +0 -2249
  149. package/dist/chunk-PBHF5WKN.js +0 -616
  150. package/dist/chunk-PTQJDMRT.js +0 -146
  151. package/dist/chunk-PZEGYCF5.js +0 -61
  152. package/dist/chunk-QBMDLBU2.js +0 -975
  153. package/dist/chunk-RQGQSLQK.js +0 -725
  154. package/dist/chunk-SDLZDHKP.js +0 -107
  155. package/dist/chunk-TNQWPPE6.js +0 -37
  156. package/dist/chunk-UHNL42EF.js +0 -2730
  157. package/dist/chunk-UNXCEF6S.js +0 -21
  158. package/dist/chunk-V2XTI523.js +0 -347
  159. package/dist/chunk-VAU366PN.js +0 -2241
  160. package/dist/chunk-VMVDTCXB.js +0 -712
  161. package/dist/chunk-VRW3FULF.js +0 -725
  162. package/dist/chunk-WADYRCO2.js +0 -304
  163. package/dist/chunk-WILQZRO4.js +0 -282
  164. package/dist/chunk-WUHJISPP.js +0 -298
  165. package/dist/chunk-XYU6TZOW.js +0 -182
  166. package/dist/chunk-Y6GP4QGG.js +0 -276
  167. package/dist/chunk-YECR7UIA.js +0 -347
  168. package/dist/chunk-YUTWTI4B.js +0 -654
  169. package/dist/chunk-Z65KYU7I.js +0 -26
  170. package/dist/chunk-Z6POF5YC.js +0 -975
  171. package/dist/chunk-ZBJP6WFL.js +0 -482
  172. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  173. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  174. package/dist/contracts-DOrhwbke.d.cts +0 -245
  175. package/dist/contracts-DOrhwbke.d.ts +0 -245
  176. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  177. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  178. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  179. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  180. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  181. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  182. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  183. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  184. package/dist/ssr-3RXHP5ES.js +0 -38
  185. package/dist/ssr-6GIMY5MX.js +0 -38
  186. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  187. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  188. package/dist/ssr-WKUPVSSK.js +0 -36
  189. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  190. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
@@ -0,0 +1,125 @@
1
+ // src/plugins/plugin.ts
2
+ function createPluginRegistry() {
3
+ const installedPlugins = /* @__PURE__ */ new Set();
4
+ const hooks = { init: [], mount: [], unmount: [], error: [] };
5
+ const provided = /* @__PURE__ */ new Map();
6
+ const registry = {
7
+ installedPlugins,
8
+ hooks,
9
+ provided,
10
+ plugin(p, options) {
11
+ if (installedPlugins.has(p.name)) {
12
+ console.warn(`[Plugin] "${p.name}" is already installed.`);
13
+ return;
14
+ }
15
+ const ctx = {
16
+ onInit: (cb) => hooks.init.push(cb),
17
+ onMount: (cb) => hooks.mount.push(cb),
18
+ onUnmount: (cb) => hooks.unmount.push(cb),
19
+ onError: (cb) => hooks.error.push(cb),
20
+ provide: (key, value) => provided.set(key, value)
21
+ };
22
+ const initHooksBefore = hooks.init.length;
23
+ p.install(ctx, options);
24
+ installedPlugins.add(p.name);
25
+ const justAdded = hooks.init.slice(initHooksBefore);
26
+ for (const cb of justAdded) {
27
+ try {
28
+ cb();
29
+ } catch (e) {
30
+ console.error(`[Plugin] "${p.name}" init error:`, e);
31
+ }
32
+ }
33
+ },
34
+ inject(key, defaultValue) {
35
+ if (provided.has(key)) return provided.get(key);
36
+ if (defaultValue !== void 0) return defaultValue;
37
+ throw new Error(`[Plugin] No provider found for key "${key}"`);
38
+ },
39
+ triggerMount(element) {
40
+ const snapshot = hooks.mount.slice();
41
+ for (const hook of snapshot) {
42
+ try {
43
+ hook(element);
44
+ } catch (e) {
45
+ console.error("[Plugin] Mount hook error:", e);
46
+ }
47
+ }
48
+ },
49
+ triggerUnmount(element) {
50
+ const snapshot = hooks.unmount.slice();
51
+ for (const hook of snapshot) {
52
+ try {
53
+ hook(element);
54
+ } catch (e) {
55
+ console.error("[Plugin] Unmount hook error:", e);
56
+ }
57
+ }
58
+ },
59
+ triggerError(error) {
60
+ const snapshot = hooks.error.slice();
61
+ for (const hook of snapshot) {
62
+ try {
63
+ hook(error);
64
+ } catch (e) {
65
+ console.error("[Plugin] Error hook error:", e);
66
+ }
67
+ }
68
+ },
69
+ reset() {
70
+ installedPlugins.clear();
71
+ hooks.init.length = 0;
72
+ hooks.mount.length = 0;
73
+ hooks.unmount.length = 0;
74
+ hooks.error.length = 0;
75
+ provided.clear();
76
+ }
77
+ };
78
+ return registry;
79
+ }
80
+ var defaultRegistry = createPluginRegistry();
81
+ var defaultRegistryTouched = false;
82
+ function createPlugin(name, install) {
83
+ return { name, install };
84
+ }
85
+ function plugin(plugin2, options) {
86
+ defaultRegistryTouched = true;
87
+ defaultRegistry.plugin(plugin2, options);
88
+ }
89
+ function inject(key, defaultValue) {
90
+ return defaultRegistry.inject(key, defaultValue);
91
+ }
92
+ function triggerPluginMount(element) {
93
+ defaultRegistry.triggerMount(element);
94
+ }
95
+ function triggerPluginUnmount(element) {
96
+ defaultRegistry.triggerUnmount(element);
97
+ }
98
+ function triggerPluginError(error) {
99
+ defaultRegistry.triggerError(error);
100
+ }
101
+ function resetPlugins() {
102
+ defaultRegistry.reset();
103
+ defaultRegistryTouched = false;
104
+ }
105
+ function setDefaultPluginRegistry(registry) {
106
+ if (defaultRegistryTouched && defaultRegistry.installedPlugins.size > 0) {
107
+ console.warn(
108
+ "[Plugin] Replacing default plugin registry while plugins are already installed on the singleton. This may indicate mixed singleton/registry usage."
109
+ );
110
+ }
111
+ defaultRegistry = registry;
112
+ defaultRegistryTouched = true;
113
+ }
114
+
115
+ export {
116
+ createPluginRegistry,
117
+ createPlugin,
118
+ plugin,
119
+ inject,
120
+ triggerPluginMount,
121
+ triggerPluginUnmount,
122
+ triggerPluginError,
123
+ resetPlugins,
124
+ setDefaultPluginRegistry
125
+ };
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  derived
3
- } from "./chunk-NEKUBFPT.js";
3
+ } from "./chunk-54EDRCEF.js";
4
4
  import {
5
5
  effect
6
- } from "./chunk-CHF5OHIA.js";
6
+ } from "./chunk-HB24TBAF.js";
7
7
  import {
8
+ batch,
8
9
  signal
9
- } from "./chunk-WZSPOOER.js";
10
+ } from "./chunk-CC65Y57T.js";
10
11
 
11
12
  // src/patterns/machine.ts
12
13
  function machine(config) {
@@ -74,7 +75,11 @@ function machine(config) {
74
75
  function persisted(key, initial, options = {}) {
75
76
  const storage = options.session ? sessionStorage : localStorage;
76
77
  const serialize = options.serialize || JSON.stringify;
77
- const deserialize = options.deserialize || JSON.parse;
78
+ const safeReviver = (k, v) => {
79
+ if (k === "__proto__" || k === "constructor" || k === "prototype") return void 0;
80
+ return v;
81
+ };
82
+ const deserialize = options.deserialize || ((raw) => JSON.parse(raw, safeReviver));
78
83
  const encrypt = options.encrypt;
79
84
  const decrypt = options.decrypt;
80
85
  const syncTabs = options.session ? false : options.syncTabs ?? true;
@@ -90,7 +95,7 @@ function persisted(key, initial, options = {}) {
90
95
  }
91
96
  const [value, setValue] = signal(restored);
92
97
  let applyingFromStorage = false;
93
- effect(() => {
98
+ const stopPersistEffect = effect(() => {
94
99
  const current = value();
95
100
  if (applyingFromStorage) return;
96
101
  try {
@@ -130,6 +135,7 @@ function persisted(key, initial, options = {}) {
130
135
  window.addEventListener("storage", storageListener);
131
136
  }
132
137
  const dispose = () => {
138
+ stopPersistEffect();
133
139
  if (storageListener && typeof window !== "undefined") {
134
140
  window.removeEventListener("storage", storageListener);
135
141
  storageListener = null;
@@ -147,62 +153,144 @@ function persisted(key, initial, options = {}) {
147
153
  // src/patterns/optimistic.ts
148
154
  function optimistic(initialValue) {
149
155
  const [value, setValue] = signal(initialValue);
150
- const [_pending, setPending] = signal(false);
151
- async function addOptimistic(optimisticValue, asyncAction) {
156
+ const [pending, setPending] = signal(false);
157
+ let inflightCount = 0;
158
+ let version = 0;
159
+ async function update(optimisticValue, asyncAction) {
160
+ const myVersion = ++version;
152
161
  const previousValue = value();
153
162
  setValue(optimisticValue);
163
+ inflightCount++;
154
164
  setPending(true);
155
165
  try {
156
166
  const result = await asyncAction();
157
- setValue(result);
167
+ if (version === myVersion) {
168
+ setValue(result);
169
+ }
158
170
  } catch {
159
- setValue(previousValue);
171
+ if (version === myVersion) {
172
+ setValue(previousValue);
173
+ }
160
174
  } finally {
161
- setPending(false);
175
+ inflightCount--;
176
+ if (inflightCount === 0) setPending(false);
162
177
  }
163
178
  }
164
- return [value, addOptimistic];
179
+ return { value, pending, update };
165
180
  }
166
181
  function optimisticList(initialValue) {
167
182
  const [items, setItems] = signal([...initialValue]);
168
- async function addOptimistic(item, asyncAction) {
183
+ const [pending, setPending] = signal(false);
184
+ let inflightCount = 0;
185
+ let version = 0;
186
+ let tempIdCounter = 0;
187
+ const itemIds = /* @__PURE__ */ new WeakMap();
188
+ const idToItem = /* @__PURE__ */ new Map();
189
+ function tagItem(item) {
190
+ const id = ++tempIdCounter;
191
+ if (item !== null && typeof item === "object") {
192
+ itemIds.set(item, id);
193
+ }
194
+ idToItem.set(id, item);
195
+ return id;
196
+ }
197
+ function findIndexById(list, id) {
198
+ for (let i = 0; i < list.length; i++) {
199
+ const it = list[i];
200
+ if (it !== null && typeof it === "object" && itemIds.get(it) === id) {
201
+ return i;
202
+ }
203
+ if (Object.is(it, idToItem.get(id))) return i;
204
+ }
205
+ return -1;
206
+ }
207
+ function begin() {
208
+ const v = ++version;
209
+ inflightCount++;
210
+ setPending(true);
211
+ return v;
212
+ }
213
+ function end(myVersion, revertFn) {
214
+ if (revertFn && version === myVersion) {
215
+ revertFn();
216
+ }
217
+ inflightCount--;
218
+ if (inflightCount === 0) setPending(false);
219
+ }
220
+ async function add(item, asyncAction) {
169
221
  const prev = items();
222
+ const id = tagItem(item);
170
223
  setItems([...prev, item]);
224
+ const myVersion = begin();
171
225
  try {
172
226
  const result = await asyncAction();
173
227
  setItems((current) => {
174
- const idx = current.lastIndexOf(item);
228
+ const idx = findIndexById(current, id);
175
229
  if (idx >= 0) {
176
230
  const next = [...current];
177
231
  next[idx] = result;
178
232
  return next;
179
233
  }
180
- return [...current.filter((i) => i !== item), result];
234
+ return [...current, result];
181
235
  });
236
+ idToItem.delete(id);
237
+ end(myVersion);
182
238
  } catch {
183
- setItems(prev);
239
+ idToItem.delete(id);
240
+ end(myVersion, () => setItems(prev));
184
241
  }
185
242
  }
186
- async function removeOptimistic(predicate, asyncAction) {
243
+ async function remove(predicate, asyncAction) {
187
244
  const prev = items();
188
245
  setItems(prev.filter((item) => !predicate(item)));
246
+ const myVersion = begin();
189
247
  try {
190
248
  await asyncAction();
249
+ end(myVersion);
191
250
  } catch {
192
- setItems(prev);
251
+ end(myVersion, () => setItems(prev));
193
252
  }
194
253
  }
195
- async function updateOptimistic(predicate, update, asyncAction) {
254
+ async function updateItem(predicate, patch, asyncAction) {
196
255
  const prev = items();
197
- setItems(prev.map((item) => predicate(item) ? { ...item, ...update } : item));
256
+ const patchedIds = [];
257
+ setItems(
258
+ prev.map((item) => {
259
+ if (predicate(item)) {
260
+ const patched = { ...item, ...patch };
261
+ const id = tagItem(patched);
262
+ patchedIds.push(id);
263
+ return patched;
264
+ }
265
+ return item;
266
+ })
267
+ );
268
+ const myVersion = begin();
198
269
  try {
199
270
  const result = await asyncAction();
200
- setItems((current) => current.map((item) => predicate(item) ? result : item));
271
+ setItems(
272
+ (current) => current.map((item) => {
273
+ if (item !== null && typeof item === "object") {
274
+ const existingId = itemIds.get(item);
275
+ if (existingId !== void 0 && patchedIds.includes(existingId)) return result;
276
+ }
277
+ return item;
278
+ })
279
+ );
280
+ for (const id of patchedIds) idToItem.delete(id);
281
+ end(myVersion);
201
282
  } catch {
202
- setItems(prev);
283
+ for (const id of patchedIds) idToItem.delete(id);
284
+ end(myVersion, () => setItems(prev));
203
285
  }
204
286
  }
205
- return { items, addOptimistic, removeOptimistic, updateOptimistic };
287
+ return {
288
+ items,
289
+ pending,
290
+ add,
291
+ remove,
292
+ update: updateItem
293
+ };
206
294
  }
207
295
 
208
296
  // src/patterns/timeTravel.ts
@@ -220,14 +308,16 @@ function timeline(initial, maxHistory = 100) {
220
308
  const idx = index();
221
309
  const newHistory = hist.slice(0, idx + 1);
222
310
  newHistory.push(newValue);
223
- if (newHistory.length > maxHistory) {
224
- newHistory.shift();
225
- setHistory(newHistory);
226
- setIndex(newHistory.length - 1);
227
- } else {
228
- setHistory(newHistory);
229
- setIndex(idx + 1);
230
- }
311
+ batch(() => {
312
+ if (newHistory.length > maxHistory) {
313
+ newHistory.shift();
314
+ setHistory(newHistory);
315
+ setIndex(newHistory.length - 1);
316
+ } else {
317
+ setHistory(newHistory);
318
+ setIndex(idx + 1);
319
+ }
320
+ });
231
321
  }
232
322
  function undo() {
233
323
  if (canUndo()) {
@@ -240,8 +330,10 @@ function timeline(initial, maxHistory = 100) {
240
330
  }
241
331
  }
242
332
  function reset() {
243
- setHistory([initial]);
244
- setIndex(0);
333
+ batch(() => {
334
+ setHistory([initial]);
335
+ setIndex(0);
336
+ });
245
337
  }
246
338
  function jumpTo(targetIndex) {
247
339
  const hist = history();
@@ -253,8 +345,37 @@ function timeline(initial, maxHistory = 100) {
253
345
  }
254
346
 
255
347
  // src/patterns/globalStore.ts
348
+ function deepClone(value) {
349
+ if (typeof structuredClone === "function") {
350
+ return structuredClone(value);
351
+ }
352
+ const seen = /* @__PURE__ */ new WeakSet();
353
+ const clone = (v) => {
354
+ if (v === null || typeof v !== "object") return v;
355
+ if (seen.has(v)) throw new Error("deepClone: circular reference");
356
+ seen.add(v);
357
+ if (v instanceof Date) return new Date(v.getTime());
358
+ if (v instanceof Map) {
359
+ const out2 = /* @__PURE__ */ new Map();
360
+ for (const [k, val] of v) out2.set(clone(k), clone(val));
361
+ return out2;
362
+ }
363
+ if (v instanceof Set) {
364
+ const out2 = /* @__PURE__ */ new Set();
365
+ for (const val of v) out2.add(clone(val));
366
+ return out2;
367
+ }
368
+ if (Array.isArray(v)) return v.map(clone);
369
+ const out = {};
370
+ for (const k of Object.keys(v)) {
371
+ out[k] = clone(v[k]);
372
+ }
373
+ return out;
374
+ };
375
+ return clone(value);
376
+ }
256
377
  function globalStore(config) {
257
- const initialState = { ...config.state };
378
+ const initialState = deepClone(config.state);
258
379
  const [getState, setState] = signal({ ...initialState });
259
380
  const listeners = /* @__PURE__ */ new Set();
260
381
  const middlewares = config.middleware || [];
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  effect
3
- } from "./chunk-CHF5OHIA.js";
3
+ } from "./chunk-HB24TBAF.js";
4
4
  import {
5
5
  batch,
6
6
  signal
7
- } from "./chunk-WZSPOOER.js";
7
+ } from "./chunk-CC65Y57T.js";
8
8
 
9
9
  // src/browser/media.ts
10
10
  function media(query) {
@@ -72,6 +72,8 @@ function scroll(target) {
72
72
  const [y, setY] = signal(0);
73
73
  const [isScrolling, setIsScrolling] = signal(false);
74
74
  let scrollTimer = null;
75
+ let currentTarget = null;
76
+ let effectCleanup = null;
75
77
  if (typeof window === "undefined") {
76
78
  return { x, y, isScrolling, dispose: () => {
77
79
  } };
@@ -94,11 +96,26 @@ function scroll(target) {
94
96
  scrollTimer = null;
95
97
  }, 150);
96
98
  };
97
- const scrollTarget = target ? target() : null;
98
- const eventTarget = scrollTarget || window;
99
- eventTarget.addEventListener("scroll", handler, { passive: true });
99
+ function attachListener(eventTarget) {
100
+ if (currentTarget === eventTarget) return;
101
+ if (currentTarget) currentTarget.removeEventListener("scroll", handler);
102
+ currentTarget = eventTarget;
103
+ currentTarget.addEventListener("scroll", handler, { passive: true });
104
+ }
105
+ if (target) {
106
+ effectCleanup = effect(() => {
107
+ const el = target();
108
+ attachListener(el || window);
109
+ });
110
+ } else {
111
+ attachListener(window);
112
+ }
100
113
  function dispose() {
101
- eventTarget.removeEventListener("scroll", handler);
114
+ effectCleanup?.();
115
+ if (currentTarget) {
116
+ currentTarget.removeEventListener("scroll", handler);
117
+ currentTarget = null;
118
+ }
102
119
  if (scrollTimer !== null) {
103
120
  clearTimeout(scrollTimer);
104
121
  scrollTimer = null;
@@ -382,7 +399,10 @@ function dropZone(element, options) {
382
399
  const raw = e.dataTransfer.getData("application/json");
383
400
  if (raw) {
384
401
  try {
385
- transferData = JSON.parse(raw);
402
+ transferData = JSON.parse(
403
+ raw,
404
+ (k, v) => k === "__proto__" || k === "constructor" || k === "prototype" ? void 0 : v
405
+ );
386
406
  } catch {
387
407
  transferData = raw;
388
408
  }
@@ -617,31 +637,44 @@ function urlState() {
617
637
  }
618
638
  };
619
639
  }
620
- const [params, setParamsSignal] = signal(new URLSearchParams(window.location.search));
621
- const [hash, setHashSignal] = signal(window.location.hash);
622
- const syncFromLocation = () => {
623
- setParamsSignal(new URLSearchParams(window.location.search));
624
- setHashSignal(window.location.hash);
625
- };
626
- const onPopState = () => syncFromLocation();
627
- window.addEventListener("popstate", onPopState);
640
+ let lastSearch = window.location.search;
641
+ let lastHash = window.location.hash;
642
+ const [params, setParamsSignal] = signal(new URLSearchParams(lastSearch));
643
+ const [hash, setHashSignal] = signal(lastHash);
644
+ function syncFromLocation() {
645
+ const currentSearch = window.location.search;
646
+ const currentHash = window.location.hash;
647
+ if (currentSearch !== lastSearch) {
648
+ lastSearch = currentSearch;
649
+ setParamsSignal(new URLSearchParams(currentSearch));
650
+ }
651
+ if (currentHash !== lastHash) {
652
+ lastHash = currentHash;
653
+ setHashSignal(currentHash);
654
+ }
655
+ }
656
+ window.addEventListener("popstate", syncFromLocation);
657
+ window.addEventListener("hashchange", syncFromLocation);
628
658
  function setParams(next, opts = {}) {
629
659
  const p = next instanceof URLSearchParams ? next : new URLSearchParams(next);
630
660
  const query = p.toString();
631
661
  const newUrl = `${window.location.pathname}${query ? `?${query}` : ""}${window.location.hash}`;
632
662
  if (opts.replace) window.history.replaceState(null, "", newUrl);
633
663
  else window.history.pushState(null, "", newUrl);
664
+ lastSearch = window.location.search;
634
665
  setParamsSignal(new URLSearchParams(p));
635
666
  }
636
667
  function setHash(next, opts = {}) {
637
- const normalized = next.startsWith("#") ? next : next ? `#${next}` : "";
668
+ const normalized = next && next !== "#" ? next.startsWith("#") ? next : `#${next}` : "";
638
669
  const newUrl = `${window.location.pathname}${window.location.search}${normalized}`;
639
670
  if (opts.replace) window.history.replaceState(null, "", newUrl);
640
671
  else window.history.pushState(null, "", newUrl);
672
+ lastHash = normalized;
641
673
  setHashSignal(normalized);
642
674
  }
643
675
  function dispose() {
644
- window.removeEventListener("popstate", onPopState);
676
+ window.removeEventListener("popstate", syncFromLocation);
677
+ window.removeEventListener("hashchange", syncFromLocation);
645
678
  }
646
679
  return { params, hash, setParams, setHash, dispose };
647
680
  }
@@ -749,13 +782,21 @@ function wakeLock() {
749
782
  }
750
783
  const onVisibility = () => {
751
784
  if (sentinel?.released && !document.hidden) {
752
- void request();
785
+ request().catch((err) => {
786
+ if (typeof console !== "undefined") {
787
+ console.warn("[SibuJS wakeLock] re-acquire failed:", err);
788
+ }
789
+ });
753
790
  }
754
791
  };
755
792
  document.addEventListener("visibilitychange", onVisibility);
756
793
  function dispose() {
757
794
  document.removeEventListener("visibilitychange", onVisibility);
758
- void release();
795
+ release().catch((err) => {
796
+ if (typeof console !== "undefined") {
797
+ console.warn("[SibuJS wakeLock] release failed:", err);
798
+ }
799
+ });
759
800
  }
760
801
  return { active, request, release, dispose };
761
802
  }
@@ -951,11 +992,21 @@ function speech() {
951
992
  };
952
993
  }
953
994
  const synth = window.speechSynthesis;
954
- const interval = setInterval(() => {
955
- setSpeaking(synth.speaking);
956
- setPaused(synth.paused);
957
- }, 200);
995
+ let interval = null;
996
+ function startPolling() {
997
+ if (interval !== null) return;
998
+ interval = setInterval(() => {
999
+ setSpeaking(synth.speaking);
1000
+ setPaused(synth.paused);
1001
+ if (!synth.speaking && !synth.paused) {
1002
+ clearInterval(interval);
1003
+ interval = null;
1004
+ }
1005
+ }, 200);
1006
+ }
1007
+ let disposed = false;
958
1008
  function speak(text, options = {}) {
1009
+ if (disposed) return;
959
1010
  const u = new SpeechSynthesisUtterance(text);
960
1011
  if (options.lang) u.lang = options.lang;
961
1012
  if (options.rate != null) u.rate = options.rate;
@@ -966,19 +1017,41 @@ function speech() {
966
1017
  const match = voices.find((v) => v.name === options.voice);
967
1018
  if (match) u.voice = match;
968
1019
  }
969
- u.addEventListener("start", () => setSpeaking(true));
970
- u.addEventListener("end", () => {
971
- setSpeaking(false);
972
- setPaused(false);
973
- });
974
- u.addEventListener("error", () => {
975
- setSpeaking(false);
976
- setPaused(false);
977
- });
1020
+ u.addEventListener(
1021
+ "start",
1022
+ () => {
1023
+ if (!disposed) setSpeaking(true);
1024
+ },
1025
+ { once: true }
1026
+ );
1027
+ u.addEventListener(
1028
+ "end",
1029
+ () => {
1030
+ if (disposed) return;
1031
+ setSpeaking(false);
1032
+ setPaused(false);
1033
+ },
1034
+ { once: true }
1035
+ );
1036
+ u.addEventListener(
1037
+ "error",
1038
+ () => {
1039
+ if (disposed) return;
1040
+ setSpeaking(false);
1041
+ setPaused(false);
1042
+ },
1043
+ { once: true }
1044
+ );
978
1045
  synth.speak(u);
1046
+ setSpeaking(true);
1047
+ startPolling();
979
1048
  }
980
1049
  function dispose() {
981
- clearInterval(interval);
1050
+ disposed = true;
1051
+ if (interval !== null) {
1052
+ clearInterval(interval);
1053
+ interval = null;
1054
+ }
982
1055
  synth.cancel();
983
1056
  }
984
1057
  return {
@@ -1207,13 +1280,21 @@ function imageLoader(src) {
1207
1280
  };
1208
1281
  img.src = url;
1209
1282
  }
1283
+ let srcEffectTeardown = null;
1210
1284
  if (typeof src === "function") {
1211
- start(src());
1285
+ srcEffectTeardown = effect(() => {
1286
+ const url = src();
1287
+ start(url);
1288
+ });
1212
1289
  } else {
1213
1290
  start(src);
1214
1291
  }
1215
1292
  function dispose() {
1216
1293
  disposed = true;
1294
+ if (srcEffectTeardown) {
1295
+ srcEffectTeardown();
1296
+ srcEffectTeardown = null;
1297
+ }
1217
1298
  if (current) {
1218
1299
  current.onload = null;
1219
1300
  current.onerror = null;