@bquery/bquery 1.8.2 → 1.10.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 (157) hide show
  1. package/README.md +255 -27
  2. package/dist/{a11y-DVBCy09c.js → a11y-DG2i4iZN.js} +3 -3
  3. package/dist/{a11y-DVBCy09c.js.map → a11y-DG2i4iZN.js.map} +1 -1
  4. package/dist/a11y.es.mjs +1 -1
  5. package/dist/{component-L3-JfOFz.js → component-DRotf1hl.js} +19 -18
  6. package/dist/{component-L3-JfOFz.js.map → component-DRotf1hl.js.map} +1 -1
  7. package/dist/component.es.mjs +1 -1
  8. package/dist/concurrency/errors.d.ts +29 -0
  9. package/dist/concurrency/errors.d.ts.map +1 -0
  10. package/dist/concurrency/high-level.d.ts +85 -0
  11. package/dist/concurrency/high-level.d.ts.map +1 -0
  12. package/dist/concurrency/index.d.ts +19 -0
  13. package/dist/concurrency/index.d.ts.map +1 -0
  14. package/dist/concurrency/internal.d.ts +26 -0
  15. package/dist/concurrency/internal.d.ts.map +1 -0
  16. package/dist/concurrency/pipeline.d.ts +30 -0
  17. package/dist/concurrency/pipeline.d.ts.map +1 -0
  18. package/dist/concurrency/pool.d.ts +48 -0
  19. package/dist/concurrency/pool.d.ts.map +1 -0
  20. package/dist/concurrency/reactive.d.ts +107 -0
  21. package/dist/concurrency/reactive.d.ts.map +1 -0
  22. package/dist/concurrency/rpc.d.ts +46 -0
  23. package/dist/concurrency/rpc.d.ts.map +1 -0
  24. package/dist/concurrency/support.d.ts +23 -0
  25. package/dist/concurrency/support.d.ts.map +1 -0
  26. package/dist/concurrency/task.d.ts +31 -0
  27. package/dist/concurrency/task.d.ts.map +1 -0
  28. package/dist/concurrency/types.d.ts +343 -0
  29. package/dist/concurrency/types.d.ts.map +1 -0
  30. package/dist/concurrency-BU1wPEsZ.js +826 -0
  31. package/dist/concurrency-BU1wPEsZ.js.map +1 -0
  32. package/dist/concurrency.es.mjs +29 -0
  33. package/dist/{constraints-D5RHQLmP.js → constraints-CqjhmpZC.js} +1 -1
  34. package/dist/{constraints-D5RHQLmP.js.map → constraints-CqjhmpZC.js.map} +1 -1
  35. package/dist/core-CongXJuo.js +87 -0
  36. package/dist/core-CongXJuo.js.map +1 -0
  37. package/dist/{custom-directives-Dr4C5lVV.js → custom-directives-BjFzFhuf.js} +1 -1
  38. package/dist/{custom-directives-Dr4C5lVV.js.map → custom-directives-BjFzFhuf.js.map} +1 -1
  39. package/dist/{devtools-BhB2iDPT.js → devtools-C5FExMwv.js} +2 -2
  40. package/dist/{devtools-BhB2iDPT.js.map → devtools-C5FExMwv.js.map} +1 -1
  41. package/dist/devtools.es.mjs +1 -1
  42. package/dist/{dnd-NwZBYh4l.js → dnd-BAqzPlSo.js} +1 -1
  43. package/dist/{dnd-NwZBYh4l.js.map → dnd-BAqzPlSo.js.map} +1 -1
  44. package/dist/dnd.es.mjs +1 -1
  45. package/dist/effect-Cc51IH91.js +87 -0
  46. package/dist/effect-Cc51IH91.js.map +1 -0
  47. package/dist/{env-CTdvLaH2.js → env-PvwYHnJq.js} +1 -1
  48. package/dist/{env-CTdvLaH2.js.map → env-PvwYHnJq.js.map} +1 -1
  49. package/dist/{forms-UcRHsYxC.js → forms-Dx1Scvh0.js} +30 -29
  50. package/dist/{forms-UcRHsYxC.js.map → forms-Dx1Scvh0.js.map} +1 -1
  51. package/dist/forms.es.mjs +1 -1
  52. package/dist/full.d.ts +6 -4
  53. package/dist/full.d.ts.map +1 -1
  54. package/dist/full.es.mjs +240 -206
  55. package/dist/full.iife.js +117 -33
  56. package/dist/full.iife.js.map +1 -1
  57. package/dist/full.umd.js +117 -33
  58. package/dist/full.umd.js.map +1 -1
  59. package/dist/{i18n-kuF6Ekj6.js → i18n-Cazyk9RD.js} +3 -3
  60. package/dist/{i18n-kuF6Ekj6.js.map → i18n-Cazyk9RD.js.map} +1 -1
  61. package/dist/i18n.es.mjs +1 -1
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.es.mjs +274 -240
  65. package/dist/media/index.d.ts +10 -3
  66. package/dist/media/index.d.ts.map +1 -1
  67. package/dist/media/observers.d.ts +99 -0
  68. package/dist/media/observers.d.ts.map +1 -0
  69. package/dist/media/types.d.ts +125 -0
  70. package/dist/media/types.d.ts.map +1 -1
  71. package/dist/media-dAKIGPk3.js +514 -0
  72. package/dist/media-dAKIGPk3.js.map +1 -0
  73. package/dist/media.es.mjs +12 -9
  74. package/dist/{motion-BJsAuULb.js → motion-BBMso9Ir.js} +1 -1
  75. package/dist/{motion-BJsAuULb.js.map → motion-BBMso9Ir.js.map} +1 -1
  76. package/dist/motion.es.mjs +1 -1
  77. package/dist/mount-C8O2vXkQ.js +450 -0
  78. package/dist/mount-C8O2vXkQ.js.map +1 -0
  79. package/dist/{platform-Dw2gE3zI.js → platform-BPHIXbw8.js} +17 -16
  80. package/dist/{platform-Dw2gE3zI.js.map → platform-BPHIXbw8.js.map} +1 -1
  81. package/dist/platform.es.mjs +1 -1
  82. package/dist/{plugin-C2WuC8SF.js → plugin-DjTqWg-P.js} +2 -2
  83. package/dist/{plugin-C2WuC8SF.js.map → plugin-DjTqWg-P.js.map} +1 -1
  84. package/dist/plugin.es.mjs +1 -1
  85. package/dist/reactive/index.d.ts +2 -2
  86. package/dist/reactive/index.d.ts.map +1 -1
  87. package/dist/reactive/signal.d.ts +2 -1
  88. package/dist/reactive/signal.d.ts.map +1 -1
  89. package/dist/reactive/watch.d.ts +49 -0
  90. package/dist/reactive/watch.d.ts.map +1 -1
  91. package/dist/reactive/websocket.d.ts +6 -3
  92. package/dist/reactive/websocket.d.ts.map +1 -1
  93. package/dist/reactive-BAd2hfl8.js +1171 -0
  94. package/dist/reactive-BAd2hfl8.js.map +1 -0
  95. package/dist/reactive.es.mjs +41 -37
  96. package/dist/readonly-C0ZwS1Tf.js +35 -0
  97. package/dist/readonly-C0ZwS1Tf.js.map +1 -0
  98. package/dist/{registry-B08iilIh.js → registry-Cr6VH8CR.js} +1 -1
  99. package/dist/{registry-B08iilIh.js.map → registry-Cr6VH8CR.js.map} +1 -1
  100. package/dist/{router-CQikC9Ed.js → router-CCepRMpC.js} +29 -28
  101. package/dist/{router-CQikC9Ed.js.map → router-CCepRMpC.js.map} +1 -1
  102. package/dist/router.es.mjs +1 -1
  103. package/dist/{ssr-_dAcGdzu.js → ssr-D-1IPcfw.js} +4 -4
  104. package/dist/{ssr-_dAcGdzu.js.map → ssr-D-1IPcfw.js.map} +1 -1
  105. package/dist/ssr.es.mjs +1 -1
  106. package/dist/{store-Cb3gPRve.js → store-CjmEeX9-.js} +6 -6
  107. package/dist/{store-Cb3gPRve.js.map → store-CjmEeX9-.js.map} +1 -1
  108. package/dist/store.es.mjs +2 -2
  109. package/dist/{testing-C5Sjfsna.js → testing-TdfaL7VE.js} +8 -8
  110. package/dist/{testing-C5Sjfsna.js.map → testing-TdfaL7VE.js.map} +1 -1
  111. package/dist/testing.es.mjs +1 -1
  112. package/dist/{untrack-D0fnO5k2.js → untrack-bjWDNdyE.js} +11 -10
  113. package/dist/{untrack-D0fnO5k2.js.map → untrack-bjWDNdyE.js.map} +1 -1
  114. package/dist/view/directives/aria.d.ts +7 -0
  115. package/dist/view/directives/aria.d.ts.map +1 -0
  116. package/dist/view/directives/error.d.ts +7 -0
  117. package/dist/view/directives/error.d.ts.map +1 -0
  118. package/dist/view/directives/index.d.ts +2 -0
  119. package/dist/view/directives/index.d.ts.map +1 -1
  120. package/dist/view/mount.d.ts.map +1 -1
  121. package/dist/view/process.d.ts +2 -0
  122. package/dist/view/process.d.ts.map +1 -1
  123. package/dist/view.es.mjs +12 -11
  124. package/package.json +18 -14
  125. package/src/concurrency/errors.ts +57 -0
  126. package/src/concurrency/high-level.ts +387 -0
  127. package/src/concurrency/index.ts +63 -0
  128. package/src/concurrency/internal.ts +100 -0
  129. package/src/concurrency/pipeline.ts +133 -0
  130. package/src/concurrency/pool.ts +450 -0
  131. package/src/concurrency/reactive.ts +339 -0
  132. package/src/concurrency/rpc.ts +380 -0
  133. package/src/concurrency/support.ts +44 -0
  134. package/src/concurrency/task.ts +318 -0
  135. package/src/concurrency/types.ts +431 -0
  136. package/src/full.ts +77 -0
  137. package/src/index.ts +3 -0
  138. package/src/media/index.ts +20 -2
  139. package/src/media/observers.ts +418 -0
  140. package/src/media/types.ts +136 -0
  141. package/src/reactive/index.ts +3 -0
  142. package/src/reactive/signal.ts +2 -1
  143. package/src/reactive/watch.ts +138 -0
  144. package/src/reactive/websocket.ts +31 -8
  145. package/src/view/directives/aria.ts +72 -0
  146. package/src/view/directives/error.ts +56 -0
  147. package/src/view/directives/index.ts +2 -0
  148. package/src/view/mount.ts +4 -0
  149. package/src/view/process.ts +6 -0
  150. package/dist/core-DdtZHzsS.js +0 -168
  151. package/dist/core-DdtZHzsS.js.map +0 -1
  152. package/dist/media-i-fB5WxI.js +0 -340
  153. package/dist/media-i-fB5WxI.js.map +0 -1
  154. package/dist/mount-B4Y8bk8Z.js +0 -403
  155. package/dist/mount-B4Y8bk8Z.js.map +0 -1
  156. package/dist/reactive-DwkhUJfP.js +0 -1148
  157. package/dist/reactive-DwkhUJfP.js.map +0 -1
@@ -6,7 +6,9 @@ import type { Computed } from './computed';
6
6
  import type { Signal } from './core';
7
7
  import type { CleanupFn } from './internals';
8
8
 
9
+ import { debounce, throttle } from '../core/utils/function';
9
10
  import { effect } from './effect';
11
+ import { getCurrentScope, onScopeDispose } from './scope';
10
12
 
11
13
  /**
12
14
  * Options for the watch function.
@@ -71,3 +73,139 @@ export const watch = <T>(
71
73
  }
72
74
  });
73
75
  };
76
+
77
+ /**
78
+ * Watches a signal or computed value and debounces callback delivery.
79
+ * Rapid changes are collapsed into a single callback using the latest value
80
+ * and the first old value observed within the debounce window.
81
+ *
82
+ * @template T - The type of the watched value
83
+ * @param source - The signal or computed to watch
84
+ * @param callback - Function called with the debounced (newValue, oldValue)
85
+ * @param delayMs - Debounce delay in milliseconds
86
+ * @param options - Watch options
87
+ * @returns A cleanup function to stop watching and cancel pending callbacks
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const query = signal('');
92
+ * const stop = watchDebounce(query, (newQuery) => {
93
+ * console.log('Search for', newQuery);
94
+ * }, 250);
95
+ *
96
+ * query.value = 'b';
97
+ * query.value = 'bq';
98
+ * query.value = 'bqu'; // Only this value is delivered after 250ms
99
+ *
100
+ * stop();
101
+ * ```
102
+ */
103
+ export const watchDebounce = <T>(
104
+ source: Signal<T> | Computed<T>,
105
+ callback: (newValue: T, oldValue: T | undefined) => void,
106
+ delayMs: number,
107
+ options: WatchOptions<T> = {}
108
+ ): CleanupFn => {
109
+ const { immediate = false, equals = Object.is } = options;
110
+ const normalizedDelayMs = Number.isFinite(delayMs) ? Math.max(0, delayMs) : 0;
111
+ let hasPending = false;
112
+ let pendingNewValue!: T;
113
+ let pendingOldValue: T | undefined;
114
+ const cancelPending = (): void => {
115
+ notify.cancel();
116
+ hasPending = false;
117
+ pendingOldValue = undefined;
118
+ };
119
+
120
+ const notify = debounce(() => {
121
+ if (!hasPending) {
122
+ return;
123
+ }
124
+
125
+ try {
126
+ callback(pendingNewValue, pendingOldValue);
127
+ } catch (error) {
128
+ console.error('bQuery reactive: Error in watchDebounce callback', error);
129
+ }
130
+ hasPending = false;
131
+ pendingOldValue = undefined;
132
+ }, normalizedDelayMs);
133
+
134
+ if (immediate) {
135
+ callback(source.peek(), undefined);
136
+ }
137
+
138
+ const cleanup = watch(
139
+ source,
140
+ (newValue, oldValue) => {
141
+ if (!hasPending) {
142
+ pendingOldValue = oldValue;
143
+ }
144
+
145
+ pendingNewValue = newValue;
146
+ hasPending = true;
147
+ notify();
148
+ },
149
+ { equals }
150
+ );
151
+
152
+ if (getCurrentScope()) {
153
+ onScopeDispose(cancelPending);
154
+ }
155
+
156
+ return () => {
157
+ cleanup();
158
+ cancelPending();
159
+ };
160
+ };
161
+
162
+ /**
163
+ * Watches a signal or computed value and throttles callback delivery.
164
+ * Changes are delivered at most once per interval.
165
+ *
166
+ * @template T - The type of the watched value
167
+ * @param source - The signal or computed to watch
168
+ * @param callback - Function called with throttled (newValue, oldValue) updates
169
+ * @param intervalMs - Minimum interval between callback runs in milliseconds
170
+ * @param options - Watch options
171
+ * @returns A cleanup function to stop watching and reset the throttle window
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * const scrollY = signal(0);
176
+ * const stop = watchThrottle(scrollY, (nextY) => {
177
+ * console.log('Scroll position', nextY);
178
+ * }, 100);
179
+ *
180
+ * stop();
181
+ * ```
182
+ */
183
+ export const watchThrottle = <T>(
184
+ source: Signal<T> | Computed<T>,
185
+ callback: (newValue: T, oldValue: T | undefined) => void,
186
+ intervalMs: number,
187
+ options: WatchOptions<T> = {}
188
+ ): CleanupFn => {
189
+ const { immediate = false, equals = Object.is } = options;
190
+ const normalizedIntervalMs = Number.isFinite(intervalMs) ? Math.max(0, intervalMs) : 0;
191
+ const notify = throttle((newValue: T, oldValue: T | undefined) => {
192
+ callback(newValue, oldValue);
193
+ }, normalizedIntervalMs);
194
+
195
+ if (immediate) {
196
+ notify(source.peek(), undefined);
197
+ }
198
+
199
+ const cleanup = watch(
200
+ source,
201
+ (newValue, oldValue) => {
202
+ notify(newValue, oldValue);
203
+ },
204
+ { equals }
205
+ );
206
+
207
+ return () => {
208
+ cleanup();
209
+ notify.cancel();
210
+ };
211
+ };
@@ -8,6 +8,9 @@
8
8
  import { computed } from './computed';
9
9
  import { Signal, signal } from './core';
10
10
 
11
+ /** @internal */
12
+ type WebSocketSendData = string | Blob | ArrayBufferLike | ArrayBufferView;
13
+
11
14
  // ---------------------------------------------------------------------------
12
15
  // Types
13
16
  // ---------------------------------------------------------------------------
@@ -40,7 +43,7 @@ export type EventSourceReconnectConfig = Pick<
40
43
  /** Configuration for keep-alive heartbeats. */
41
44
  export interface WebSocketHeartbeatConfig {
42
45
  /** Outgoing ping message (default: `'ping'`). */
43
- message?: string | ArrayBufferLike | Blob | ArrayBufferView;
46
+ message?: WebSocketSendData;
44
47
  /** Interval in ms between heartbeat pings (default: 30 000). */
45
48
  interval?: number;
46
49
  /** Time in ms to wait for a pong before assuming the connection is dead (default: 10 000). */
@@ -52,7 +55,7 @@ export interface WebSocketHeartbeatConfig {
52
55
  /** Serializer/deserializer for typed messaging. */
53
56
  export interface WebSocketSerializer<TSend = unknown, TReceive = unknown> {
54
57
  /** Serialize a value before sending over the wire. Default: `JSON.stringify`. */
55
- serialize?: (data: TSend) => string | ArrayBufferLike | Blob | ArrayBufferView;
58
+ serialize?: (data: TSend) => WebSocketSendData;
56
59
  /** Deserialize an incoming message. Default: `JSON.parse`. */
57
60
  deserialize?: (event: MessageEvent) => TReceive;
58
61
  }
@@ -115,7 +118,7 @@ export interface UseWebSocketReturn<TSend = unknown, TReceive = unknown> {
115
118
  * Uses the same queuing behavior as {@link send}: data is queued when the
116
119
  * socket is not `OPEN` and flushed once a connection is (re)established.
117
120
  */
118
- sendRaw: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;
121
+ sendRaw: (data: WebSocketSendData) => void;
119
122
  /** Manually open / reconnect the WebSocket. */
120
123
  open: () => void;
121
124
  /** Gracefully close the connection. */
@@ -162,6 +165,26 @@ const computeDelay = (attempt: number, config: WebSocketReconnectConfig): number
162
165
  return Math.min(base * factor ** attempt, max);
163
166
  };
164
167
 
168
+ /** @internal */
169
+ const sendSocketData = (socket: WebSocket, data: WebSocketSendData): void => {
170
+ if (typeof data === 'string' || data instanceof Blob || data instanceof ArrayBuffer) {
171
+ socket.send(data);
172
+ return;
173
+ }
174
+
175
+ if (ArrayBuffer.isView(data)) {
176
+ socket.send(data as BufferSource);
177
+ return;
178
+ }
179
+
180
+ if (typeof SharedArrayBuffer !== 'undefined' && data instanceof SharedArrayBuffer) {
181
+ socket.send(data as unknown as BufferSource);
182
+ return;
183
+ }
184
+
185
+ throw new TypeError('Unsupported WebSocket payload type.');
186
+ };
187
+
165
188
  // ---------------------------------------------------------------------------
166
189
  // useWebSocket
167
190
  // ---------------------------------------------------------------------------
@@ -249,7 +272,7 @@ export const useWebSocket = <TSend = string, TReceive = string>(
249
272
  let internalReconnectCount = 0;
250
273
  let isAutoReconnecting = false;
251
274
  let pingSentAt = 0;
252
- const sendQueue: Array<string | ArrayBufferLike | Blob | ArrayBufferView> = [];
275
+ const sendQueue: WebSocketSendData[] = [];
253
276
 
254
277
  const reconnectConfig = resolveReconnect(options.autoReconnect);
255
278
  const heartbeatConfig = resolveHeartbeat(options.heartbeat);
@@ -265,7 +288,7 @@ export const useWebSocket = <TSend = string, TReceive = string>(
265
288
  heartbeatTimer = setInterval(() => {
266
289
  if (ws?.readyState === WebSocket.OPEN) {
267
290
  pingSentAt = Date.now();
268
- ws.send(pingMsg);
291
+ sendSocketData(ws, pingMsg);
269
292
  if (pongTimer !== undefined) {
270
293
  clearTimeout(pongTimer);
271
294
  }
@@ -341,7 +364,7 @@ export const useWebSocket = <TSend = string, TReceive = string>(
341
364
  if (ws.readyState !== WebSocket.OPEN) {
342
365
  break;
343
366
  }
344
- ws.send(sendQueue[index]);
367
+ sendSocketData(ws, sendQueue[index]);
345
368
  }
346
369
 
347
370
  if (index > 0) {
@@ -460,10 +483,10 @@ export const useWebSocket = <TSend = string, TReceive = string>(
460
483
  sendRaw(serialized);
461
484
  };
462
485
 
463
- const sendRaw = (raw: string | ArrayBufferLike | Blob | ArrayBufferView): void => {
486
+ const sendRaw = (raw: WebSocketSendData): void => {
464
487
  if (disposed) return;
465
488
  if (ws?.readyState === WebSocket.OPEN) {
466
- ws.send(raw);
489
+ sendSocketData(ws, raw);
467
490
  } else {
468
491
  sendQueue.push(raw);
469
492
  }
@@ -0,0 +1,72 @@
1
+ import { effect } from '../../reactive/index';
2
+ import { isPrototypePollutionKey } from '../../core/utils/object';
3
+ import { evaluate, parseObjectExpression } from '../evaluate';
4
+ import type { DirectiveHandler } from '../types';
5
+
6
+ const toKebabCase = (value: string): string => value.replace(/([A-Z])/g, '-$1').toLowerCase();
7
+
8
+ const normalizeAriaAttribute = (name: string): string => {
9
+ const trimmed = name.trim();
10
+ const lower = trimmed.toLowerCase();
11
+
12
+ if (lower.startsWith('aria-')) {
13
+ return lower;
14
+ }
15
+
16
+ const withoutPrefix = /^aria[A-Z]/.test(trimmed) ? trimmed.slice(4) : trimmed;
17
+ return `aria-${toKebabCase(withoutPrefix).replace(/^-/, '')}`;
18
+ };
19
+
20
+ const shouldRemoveAttribute = (value: unknown): boolean => value == null || value === '';
21
+
22
+ /**
23
+ * Handles bq-aria directive - reactive ARIA attribute binding.
24
+ * @internal
25
+ */
26
+ export const handleAria: DirectiveHandler = (el, expression, context, cleanups) => {
27
+ let appliedAttributes: Set<string> = new Set();
28
+
29
+ const cleanup = effect(() => {
30
+ const newAttributes = new Set<string>();
31
+ const ariaValues = Object.create(null) as Record<string, unknown>;
32
+
33
+ if (expression.trimStart().startsWith('{')) {
34
+ const ariaMap = parseObjectExpression(expression);
35
+ for (const [attrName, valueExpr] of Object.entries(ariaMap)) {
36
+ ariaValues[attrName] = evaluate(valueExpr, context);
37
+ }
38
+ } else {
39
+ const result = evaluate<Record<string, unknown>>(expression, context);
40
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
41
+ for (const [attrName, value] of Object.entries(result)) {
42
+ if (isPrototypePollutionKey(attrName)) {
43
+ continue;
44
+ }
45
+
46
+ ariaValues[attrName] = value;
47
+ }
48
+ }
49
+ }
50
+
51
+ for (const [attrName, value] of Object.entries(ariaValues)) {
52
+ const normalizedName = normalizeAriaAttribute(attrName);
53
+ if (shouldRemoveAttribute(value)) {
54
+ el.removeAttribute(normalizedName);
55
+ continue;
56
+ }
57
+
58
+ el.setAttribute(normalizedName, value === true ? 'true' : String(value));
59
+ newAttributes.add(normalizedName);
60
+ }
61
+
62
+ for (const attrName of appliedAttributes) {
63
+ if (!newAttributes.has(attrName)) {
64
+ el.removeAttribute(attrName);
65
+ }
66
+ }
67
+
68
+ appliedAttributes = newAttributes;
69
+ });
70
+
71
+ cleanups.push(cleanup);
72
+ };
@@ -0,0 +1,56 @@
1
+ import { effect, isComputed, isSignal } from '../../reactive/index';
2
+ import { evaluate } from '../evaluate';
3
+ import type { DirectiveHandler } from '../types';
4
+
5
+ type ErrorSource = unknown;
6
+
7
+ const getErrorMessage = (source: ErrorSource): string => {
8
+ if (isSignal(source) || isComputed(source)) {
9
+ return String(source.value ?? '');
10
+ }
11
+
12
+ if (source && typeof source === 'object' && 'error' in source) {
13
+ const errorValue = source.error;
14
+ if (isSignal(errorValue) || isComputed(errorValue)) {
15
+ return String(errorValue.value ?? '');
16
+ }
17
+ return String(errorValue ?? '');
18
+ }
19
+
20
+ return String(source ?? '');
21
+ };
22
+
23
+ /**
24
+ * Handles bq-error directive - renders error messages and toggles visibility.
25
+ * @internal
26
+ */
27
+ export const handleError: DirectiveHandler = (el, expression, context, cleanups) => {
28
+ const htmlEl = el as HTMLElement;
29
+ const shouldManageAriaHidden = !htmlEl.hasAttribute('aria-hidden');
30
+
31
+ if (!htmlEl.hasAttribute('role')) {
32
+ htmlEl.setAttribute('role', 'alert');
33
+ }
34
+
35
+ if (!htmlEl.hasAttribute('aria-live')) {
36
+ htmlEl.setAttribute('aria-live', 'assertive');
37
+ }
38
+
39
+ const cleanup = effect(() => {
40
+ const source = evaluate(expression, context);
41
+ const message = getErrorMessage(source).trim();
42
+ const hasMessage = message.length > 0;
43
+
44
+ htmlEl.textContent = message;
45
+ htmlEl.hidden = !hasMessage;
46
+ if (shouldManageAriaHidden) {
47
+ if (hasMessage) {
48
+ htmlEl.removeAttribute('aria-hidden');
49
+ } else {
50
+ htmlEl.setAttribute('aria-hidden', 'true');
51
+ }
52
+ }
53
+ });
54
+
55
+ cleanups.push(cleanup);
56
+ };
@@ -1,5 +1,7 @@
1
+ export { handleAria } from './aria';
1
2
  export { handleBind } from './bind';
2
3
  export { handleClass } from './class';
4
+ export { handleError } from './error';
3
5
  export { createForHandler } from './for';
4
6
  export { handleHtml } from './html';
5
7
  export { handleIf } from './if';
package/src/view/mount.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { CleanupFn } from '../reactive/index';
2
2
  import {
3
3
  createForHandler,
4
+ handleAria,
4
5
  handleBind,
5
6
  handleClass,
7
+ handleError,
6
8
  handleHtml,
7
9
  handleIf,
8
10
  handleModel,
@@ -80,6 +82,8 @@ export const mount = (
80
82
 
81
83
  const handlers: DirectiveHandlers = {
82
84
  text: handleText,
85
+ error: handleError,
86
+ aria: handleAria,
83
87
  html: handleHtml(sanitize),
84
88
  if: handleIf,
85
89
  show: handleShow,
@@ -5,6 +5,8 @@ import type { BindingContext, DirectiveHandler } from './types';
5
5
 
6
6
  export type DirectiveHandlers = {
7
7
  text: DirectiveHandler;
8
+ error: DirectiveHandler;
9
+ aria: DirectiveHandler;
8
10
  html: DirectiveHandler;
9
11
  if: DirectiveHandler;
10
12
  show: DirectiveHandler;
@@ -46,6 +48,10 @@ export const processElement = (
46
48
  // Handle other directives
47
49
  if (directive === 'text') {
48
50
  handlers.text(el, value, context, cleanups);
51
+ } else if (directive === 'error') {
52
+ handlers.error(el, value, context, cleanups);
53
+ } else if (directive === 'aria') {
54
+ handlers.aria(el, value, context, cleanups);
49
55
  } else if (directive === 'html') {
50
56
  handlers.html(el, value, context, cleanups);
51
57
  } else if (directive === 'if') {
@@ -1,168 +0,0 @@
1
- var t = [], o = 0, p = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new WeakMap(), y = (e, r) => {
2
- t.push(e);
3
- try {
4
- return r();
5
- } finally {
6
- t.pop();
7
- }
8
- }, g = () => t[t.length - 1], A = (e) => {
9
- t.push(void 0);
10
- try {
11
- return e();
12
- } finally {
13
- t.pop();
14
- }
15
- }, S = (e) => {
16
- if (o > 0) {
17
- p.add(e);
18
- return;
19
- }
20
- e();
21
- }, _ = () => {
22
- for (const e of Array.from(p)) {
23
- p.delete(e);
24
- try {
25
- e();
26
- } catch (r) {
27
- console.error("bQuery reactive: Error in observer during batch flush", r);
28
- }
29
- }
30
- }, O = () => {
31
- o += 1;
32
- }, C = () => {
33
- o <= 0 || (o -= 1, o === 0 && _());
34
- }, w = (e, r) => {
35
- let s = n.get(e);
36
- s || (s = /* @__PURE__ */ new Set(), n.set(e, s)), s.add(r);
37
- }, D = (e, r) => {
38
- const s = n.get(e);
39
- s && s.delete(r);
40
- }, d = (e) => {
41
- const r = n.get(e);
42
- if (r) {
43
- for (const s of r) s.unsubscribe(e);
44
- r.clear();
45
- }
46
- }, c = [], f = (e) => typeof e == "object" && e !== null && "_addDisposer" in e, m = (e) => (typeof e == "object" || typeof e == "function") && e !== null && typeof e.then == "function", E = (e) => {
47
- const r = typeof e.constructor == "function" ? e.constructor.name : void 0;
48
- return typeof e == "function" && (Symbol.toStringTag in e && e[Symbol.toStringTag] === "AsyncFunction" || r === "AsyncFunction");
49
- }, a = () => {
50
- for (let e = c.length - 1; e >= 0; e--) if (c[e].active) return c[e];
51
- }, k = class {
52
- constructor() {
53
- this.disposers = [], this._active = !0;
54
- }
55
- get active() {
56
- return this._active;
57
- }
58
- _addDisposer(e) {
59
- this._active && this.disposers.push(e);
60
- }
61
- run(e) {
62
- if (!this._active) throw new Error("bQuery reactive: Cannot run in a stopped effectScope");
63
- if (E(e)) throw new Error("bQuery reactive: effectScope.run() only supports synchronous callbacks");
64
- c.push(this);
65
- try {
66
- const r = e();
67
- if (m(r))
68
- throw this.stop(), new Error("bQuery reactive: effectScope.run() only supports synchronous callbacks");
69
- return r;
70
- } finally {
71
- c.pop();
72
- }
73
- }
74
- stop() {
75
- if (this._active) {
76
- this._active = !1;
77
- for (let e = this.disposers.length - 1; e >= 0; e--) try {
78
- this.disposers[e]();
79
- } catch (r) {
80
- console.error("bQuery reactive: Error in scope cleanup", r);
81
- }
82
- this.disposers.length = 0;
83
- }
84
- }
85
- }, F = () => {
86
- const e = new k(), r = a();
87
- return f(r) && r._addDisposer(() => e.stop()), e;
88
- }, j = () => a(), B = (e) => {
89
- const r = a();
90
- if (!r || !r.active || !f(r)) throw new Error("bQuery reactive: onScopeDispose() must be called inside an active effectScope");
91
- r._addDisposer(e);
92
- }, T = (e) => {
93
- let r, s = !1;
94
- const h = a(), l = () => {
95
- if (r) {
96
- try {
97
- r();
98
- } catch (u) {
99
- console.error("bQuery reactive: Error in effect cleanup", u);
100
- }
101
- r = void 0;
102
- }
103
- }, v = () => {
104
- l(), d(i);
105
- }, b = () => {
106
- s || (s = !0, v());
107
- };
108
- f(h) && h._addDisposer(b);
109
- const i = () => {
110
- if (!s) {
111
- l(), d(i);
112
- try {
113
- r = y(i, e);
114
- } catch (u) {
115
- console.error("bQuery reactive: Error in effect", u);
116
- }
117
- s && v();
118
- }
119
- };
120
- return i(), b;
121
- }, Q = class {
122
- constructor(e) {
123
- this._value = e, this.subscribers = /* @__PURE__ */ new Set();
124
- }
125
- get value() {
126
- const e = g();
127
- return e && (this.subscribers.add(e), w(e, this)), this._value;
128
- }
129
- set value(e) {
130
- if (Object.is(this._value, e)) return;
131
- this._value = e;
132
- const r = Array.from(this.subscribers);
133
- for (const s of r) S(s);
134
- }
135
- peek() {
136
- return this._value;
137
- }
138
- update(e) {
139
- this.value = e(this._value);
140
- }
141
- dispose() {
142
- for (const e of this.subscribers) D(e, this);
143
- this.subscribers.clear();
144
- }
145
- unsubscribe(e) {
146
- this.subscribers.delete(e);
147
- }
148
- }, I = (e) => new Q(e);
149
- export {
150
- a,
151
- B as c,
152
- C as d,
153
- g as f,
154
- A as g,
155
- y as h,
156
- F as i,
157
- O as l,
158
- S as m,
159
- I as n,
160
- j as o,
161
- w as p,
162
- T as r,
163
- f as s,
164
- Q as t,
165
- d as u
166
- };
167
-
168
- //# sourceMappingURL=core-DdtZHzsS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"core-DdtZHzsS.js","names":[],"sources":["../src/reactive/internals.ts","../src/reactive/scope.ts","../src/reactive/effect.ts","../src/reactive/core.ts"],"sourcesContent":["/**\n * Internal reactive plumbing shared across primitives.\n * @internal\n */\n\nexport type Observer = () => void;\nexport type CleanupFn = () => void;\n\n/**\n * Interface for reactive sources (Signals, Computed) that can unsubscribe observers.\n * @internal\n */\nexport interface ReactiveSource {\n unsubscribe(observer: Observer): void;\n}\n\nconst observerStack: Observer[] = [];\nlet batchDepth = 0;\nconst pendingObservers = new Set<Observer>();\n\n// Track dependencies for each observer to enable cleanup\nconst observerDependencies = new WeakMap<Observer, Set<ReactiveSource>>();\n\nexport const track = <T>(observer: Observer, fn: () => T): T => {\n observerStack.push(observer);\n try {\n return fn();\n } finally {\n observerStack.pop();\n }\n};\n\nexport const getCurrentObserver = (): Observer | undefined =>\n observerStack[observerStack.length - 1];\n\n/**\n * Executes a function without exposing the current observer to dependencies.\n * Unlike disabling tracking globally, this still allows nested reactive internals\n * (e.g., computed recomputation) to track their own dependencies.\n * @internal\n */\nexport const withoutCurrentObserver = <T>(fn: () => T): T => {\n // Push undefined to temporarily \"hide\" the current observer\n // This way, Signal.value reads won't link to the previous observer,\n // but nested track() calls (e.g., computed recompute) still work normally.\n observerStack.push(undefined as unknown as Observer);\n try {\n return fn();\n } finally {\n observerStack.pop();\n }\n};\n\nexport const scheduleObserver = (observer: Observer): void => {\n if (batchDepth > 0) {\n pendingObservers.add(observer);\n return;\n }\n observer();\n};\n\nconst flushObservers = (): void => {\n for (const observer of Array.from(pendingObservers)) {\n pendingObservers.delete(observer);\n try {\n observer();\n } catch (error) {\n console.error('bQuery reactive: Error in observer during batch flush', error);\n }\n }\n};\n\nexport const beginBatch = (): void => {\n batchDepth += 1;\n};\n\nexport const endBatch = (): void => {\n if (batchDepth <= 0) return;\n batchDepth -= 1;\n if (batchDepth === 0) {\n flushObservers();\n }\n};\n\n/**\n * Registers a dependency between an observer and a reactive source.\n * @internal\n */\nexport const registerDependency = (observer: Observer, source: ReactiveSource): void => {\n let deps = observerDependencies.get(observer);\n if (!deps) {\n deps = new Set();\n observerDependencies.set(observer, deps);\n }\n deps.add(source);\n};\n\n/**\n * Removes a specific source from an observer's dependency set.\n * Used when a source (e.g. Signal) is disposed to prevent stale references.\n * @internal\n */\nexport const removeDependency = (observer: Observer, source: ReactiveSource): void => {\n const deps = observerDependencies.get(observer);\n if (deps) {\n deps.delete(source);\n }\n};\n\n/**\n * Clears all dependencies for an observer, unsubscribing from all sources.\n * @internal\n */\nexport const clearDependencies = (observer: Observer): void => {\n const deps = observerDependencies.get(observer);\n if (deps) {\n for (const source of deps) {\n source.unsubscribe(observer);\n }\n deps.clear();\n }\n};\n","/**\n * Reactive effect scopes for grouped disposal.\n *\n * An `EffectScope` collects all effects, computed values, and watches created\n * inside its `run()` callback so they can be disposed together with a single\n * `stop()` call. Scopes nest — an inner scope is collected by its parent.\n *\n * @module bquery/reactive\n */\n\nimport type { CleanupFn } from './internals';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A scope that collects reactive resources for grouped disposal.\n *\n * @example\n * ```ts\n * import { effectScope, signal, effect, computed } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const count = signal(0);\n * effect(() => console.log(count.value));\n * const doubled = computed(() => count.value * 2);\n * });\n *\n * scope.stop(); // All effects and computed values disposed\n * ```\n */\nexport interface EffectScope {\n /** Whether the scope has not yet been stopped. */\n readonly active: boolean;\n\n /**\n * Executes `fn` inside this scope, collecting any reactive resources\n * (effects, computed values, watches, nested scopes) created during the call.\n *\n * `run()` is synchronous-only. Do not pass an async function or a function\n * that returns a Promise — resources created after an `await` cannot be\n * collected reliably.\n *\n * @template T - Return type of the provided function\n * @param fn - Function to run inside the scope\n * @returns The return value of `fn`\n * @throws {Error} If the scope has already been stopped\n */\n run<T>(fn: () => T): T;\n\n /**\n * Disposes all collected resources and marks the scope as inactive.\n * Calling `stop()` on an already-stopped scope is a safe no-op.\n */\n stop(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal scope stack\n// ---------------------------------------------------------------------------\n\n/** @internal */\ninterface ScopeInternal extends EffectScope {\n /** @internal – Register a cleanup callback to run when the scope stops. */\n _addDisposer(fn: CleanupFn): void;\n}\n\nconst scopeStack: ScopeInternal[] = [];\n\n/** @internal */\nexport const hasScopeDisposer = (\n scope: EffectScope | undefined\n): scope is EffectScope & { _addDisposer(fn: CleanupFn): void } =>\n typeof scope === 'object' && scope !== null && '_addDisposer' in scope;\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n typeof (value as { then?: unknown }).then === 'function';\n\n/**\n * Best-effort detection for native async functions so `run()` can reject them\n * before invocation. Transpiled async functions may not preserve this shape, so\n * promise-like return values are still checked after execution as a fallback.\n * @internal\n */\nconst isAsyncFunction = (value: unknown): value is (...args: never[]) => Promise<unknown> => {\n const constructorName =\n typeof (value as { constructor?: unknown }).constructor === 'function'\n ? (value as { constructor: { name?: unknown } }).constructor.name\n : undefined;\n\n return (\n typeof value === 'function' &&\n ((Symbol.toStringTag in value &&\n (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag] === 'AsyncFunction') ||\n constructorName === 'AsyncFunction')\n );\n};\n\n/**\n * Returns the currently active scope, or `undefined` if none.\n * @internal\n */\nexport const getActiveScope = (): EffectScope | undefined => {\n for (let i = scopeStack.length - 1; i >= 0; i--) {\n if (scopeStack[i].active) {\n return scopeStack[i];\n }\n }\n\n return undefined;\n};\n\n// ---------------------------------------------------------------------------\n// EffectScope implementation\n// ---------------------------------------------------------------------------\n\nclass EffectScopeImpl implements ScopeInternal {\n private disposers: CleanupFn[] = [];\n private _active = true;\n\n get active(): boolean {\n return this._active;\n }\n\n /** @internal */\n _addDisposer(fn: CleanupFn): void {\n if (this._active) {\n this.disposers.push(fn);\n }\n }\n\n run<T>(fn: () => T): T {\n if (!this._active) {\n throw new Error('bQuery reactive: Cannot run in a stopped effectScope');\n }\n if (isAsyncFunction(fn)) {\n throw new Error('bQuery reactive: effectScope.run() only supports synchronous callbacks');\n }\n\n scopeStack.push(this);\n try {\n const result = fn();\n if (isPromiseLike(result)) {\n this.stop();\n throw new Error('bQuery reactive: effectScope.run() only supports synchronous callbacks');\n }\n return result;\n } finally {\n scopeStack.pop();\n }\n }\n\n stop(): void {\n if (!this._active) return;\n this._active = false;\n\n // Dispose in reverse order (LIFO) to mirror creation order\n for (let i = this.disposers.length - 1; i >= 0; i--) {\n try {\n this.disposers[i]();\n } catch (error) {\n console.error('bQuery reactive: Error in scope cleanup', error);\n }\n }\n this.disposers.length = 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a new effect scope for grouped disposal of reactive resources.\n *\n * All `effect()`, `computed()`, `watch()`, and nested `effectScope()` calls\n * made inside `scope.run(fn)` are automatically collected. Calling\n * `scope.stop()` disposes them all at once.\n *\n * `run()` is synchronous-only. Create the scope outside async flows when\n * needed, but keep the callback itself synchronous so cleanup registration\n * stays deterministic.\n *\n * @returns A new {@link EffectScope}\n *\n * @example\n * ```ts\n * import { effectScope, signal, effect, onScopeDispose } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const count = signal(0);\n *\n * effect(() => console.log(count.value));\n *\n * onScopeDispose(() => {\n * console.log('Custom cleanup');\n * });\n * });\n *\n * scope.stop(); // logs \"Custom cleanup\", all effects stopped\n * ```\n */\nexport const effectScope = (): EffectScope => {\n const scope = new EffectScopeImpl();\n\n // If created inside another scope, auto-collect as a nested scope\n const parent = getActiveScope();\n if (hasScopeDisposer(parent)) {\n parent._addDisposer(() => scope.stop());\n }\n\n return scope;\n};\n\n/**\n * Returns the currently active {@link EffectScope}, or `undefined` if\n * code is not running inside any scope's `run()` callback.\n *\n * @returns The active scope, or `undefined`\n *\n * @example\n * ```ts\n * import { effectScope, getCurrentScope } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n * scope.run(() => {\n * console.log(getCurrentScope() !== undefined); // true\n * });\n *\n * console.log(getCurrentScope()); // undefined\n * ```\n */\nexport const getCurrentScope = (): EffectScope | undefined => getActiveScope();\n\n/**\n * Registers a cleanup callback on the currently active scope.\n *\n * The callback runs when the scope is stopped. This is useful for\n * registering arbitrary cleanup (e.g. event listeners, timers)\n * alongside effects and computed values.\n *\n * @param fn - Cleanup function to run when the scope stops\n * @throws {Error} If called outside an active scope\n *\n * @example\n * ```ts\n * import { effectScope, onScopeDispose } from '@bquery/bquery/reactive';\n *\n * const scope = effectScope();\n *\n * scope.run(() => {\n * const controller = new AbortController();\n * fetch('/api/data', { signal: controller.signal });\n *\n * onScopeDispose(() => controller.abort());\n * });\n *\n * scope.stop(); // abort() is called\n * ```\n */\nexport const onScopeDispose = (fn: CleanupFn): void => {\n const scope = getActiveScope();\n if (!scope || !scope.active || !hasScopeDisposer(scope)) {\n throw new Error(\n 'bQuery reactive: onScopeDispose() must be called inside an active effectScope'\n );\n }\n scope._addDisposer(fn);\n};\n","/**\n * Reactive effects.\n */\n\nimport { CleanupFn, Observer, track, clearDependencies } from './internals';\nimport { getActiveScope, hasScopeDisposer } from './scope';\n\n/**\n * Creates a side effect that automatically re-runs when dependencies change.\n *\n * The effect runs immediately upon creation and then re-runs whenever\n * any signal or computed value read inside it changes.\n *\n * If created inside an {@link effectScope}, the effect is automatically\n * collected and will be disposed when the scope stops.\n *\n * @param fn - The effect function to run\n * @returns A cleanup function to stop the effect\n */\nexport const effect = (fn: () => void | CleanupFn): CleanupFn => {\n let cleanupFn: CleanupFn | void;\n let isDisposed = false;\n const scope = getActiveScope();\n\n const runCleanup = (): void => {\n if (cleanupFn) {\n try {\n cleanupFn();\n } catch (error) {\n console.error('bQuery reactive: Error in effect cleanup', error);\n }\n cleanupFn = undefined;\n }\n };\n\n const clearEffectState = (): void => {\n runCleanup();\n // Clean up all dependencies when effect is disposed\n clearDependencies(observer);\n };\n\n const dispose: CleanupFn = () => {\n if (isDisposed) {\n return;\n }\n\n isDisposed = true;\n clearEffectState();\n };\n\n if (hasScopeDisposer(scope)) {\n scope._addDisposer(dispose);\n }\n\n const observer: Observer = () => {\n if (isDisposed) return;\n\n runCleanup();\n\n // Clear old dependencies before running to avoid stale subscriptions\n clearDependencies(observer);\n\n try {\n cleanupFn = track(observer, fn);\n } catch (error) {\n console.error('bQuery reactive: Error in effect', error);\n }\n\n if (isDisposed) {\n clearEffectState();\n }\n };\n\n observer();\n\n return dispose;\n};\n","/**\n * Core reactive signals.\n */\n\nimport {\n getCurrentObserver,\n registerDependency,\n removeDependency,\n scheduleObserver,\n type ReactiveSource,\n} from './internals';\n\n/**\n * A reactive value container that notifies subscribers on change.\n *\n * Signals are the foundational primitive of the reactive system.\n * Reading a signal's value inside an effect or computed automatically\n * establishes a reactive dependency.\n *\n * @template T - The type of the stored value\n */\nexport class Signal<T> implements ReactiveSource {\n private subscribers = new Set<() => void>();\n\n /**\n * Creates a new signal with an initial value.\n * @param _value - The initial value\n */\n constructor(private _value: T) {}\n\n /**\n * Gets the current value and tracks the read if inside an observer.\n * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.\n */\n get value(): T {\n const current = getCurrentObserver();\n if (current) {\n this.subscribers.add(current);\n registerDependency(current, this);\n }\n return this._value;\n }\n\n /**\n * Sets a new value and notifies all subscribers if the value changed.\n * Uses Object.is for equality comparison.\n */\n set value(next: T) {\n if (Object.is(this._value, next)) return;\n this._value = next;\n // Create snapshot to avoid issues with subscribers modifying the set during iteration\n const subscribersSnapshot = Array.from(this.subscribers);\n for (const subscriber of subscribersSnapshot) {\n scheduleObserver(subscriber);\n }\n }\n\n /**\n * Reads the current value without tracking.\n * Useful when you need the value but don't want to create a dependency.\n *\n * @returns The current value\n */\n peek(): T {\n return this._value;\n }\n\n /**\n * Updates the value using a function.\n * Useful for updates based on the current value.\n *\n * @param updater - Function that receives current value and returns new value\n */\n update(updater: (current: T) => T): void {\n this.value = updater(this._value);\n }\n\n /**\n * Removes all subscribers from this signal.\n * Use this when a signal is no longer needed to prevent memory leaks.\n *\n * @example\n * ```ts\n * const count = signal(0);\n * effect(() => console.log(count.value));\n * count.dispose(); // All subscribers removed\n * ```\n */\n dispose(): void {\n // Remove this signal from each subscriber's dependency set\n // so the observer no longer holds a strong reference to it\n for (const subscriber of this.subscribers) {\n removeDependency(subscriber, this);\n }\n this.subscribers.clear();\n }\n\n /**\n * Removes an observer from this signal's subscriber set.\n * @internal\n */\n unsubscribe(observer: () => void): void {\n this.subscribers.delete(observer);\n }\n}\n\n/**\n * Creates a new reactive signal.\n *\n * @template T - The type of the signal value\n * @param value - The initial value\n * @returns A new Signal instance\n */\nexport const signal = <T>(value: T): Signal<T> => new Signal(value);\n"],"mappings":"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"}