@nerdalytics/beacon 1000.2.0 → 1000.2.1

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.
@@ -8,36 +8,12 @@ declare const STATE_ID: unique symbol;
8
8
  export type State<T> = ReadOnlyState<T> & WriteableState<T> & {
9
9
  [STATE_ID]?: symbol;
10
10
  };
11
- /**
12
- * Creates a reactive state container with the provided initial value.
13
- */
14
11
  export declare const state: <T>(initialValue: T, equalityFn?: (a: T, b: T) => boolean) => State<T>;
15
- /**
16
- * Registers a function to run whenever its reactive dependencies change.
17
- */
18
12
  export declare const effect: (fn: () => void) => Unsubscribe;
19
- /**
20
- * Groups multiple state updates to trigger effects only once at the end.
21
- */
22
13
  export declare const batch: <T>(fn: () => T) => T;
23
- /**
24
- * Creates a read-only computed value that updates when its dependencies change.
25
- */
26
14
  export declare const derive: <T>(computeFn: () => T) => ReadOnlyState<T>;
27
- /**
28
- * Creates an efficient subscription to a subset of a state value.
29
- */
30
15
  export declare const select: <T, R>(source: ReadOnlyState<T>, selectorFn: (state: T) => R, equalityFn?: (a: R, b: R) => boolean) => ReadOnlyState<R>;
31
- /**
32
- * Creates a read-only view of a state, hiding mutation methods.
33
- */
34
16
  export declare const readonlyState: <T>(state: State<T>) => ReadOnlyState<T>;
35
- /**
36
- * Creates a state with access control, returning a tuple of reader and writer.
37
- */
38
17
  export declare const protectedState: <T>(initialValue: T, equalityFn?: (a: T, b: T) => boolean) => [ReadOnlyState<T>, WriteableState<T>];
39
- /**
40
- * Creates a lens for direct updates to nested properties of a state.
41
- */
42
18
  export declare const lens: <T, K>(source: State<T>, accessor: (state: T) => K) => State<K>;
43
19
  export {};
package/dist/src/index.js CHANGED
@@ -1,32 +1,10 @@
1
- // Special symbol used for internal tracking
2
1
  const STATE_ID = Symbol();
3
- /**
4
- * Creates a reactive state container with the provided initial value.
5
- */
6
2
  export const state = (initialValue, equalityFn = Object.is) => StateImpl.createState(initialValue, equalityFn);
7
- /**
8
- * Registers a function to run whenever its reactive dependencies change.
9
- */
10
3
  export const effect = (fn) => StateImpl.createEffect(fn);
11
- /**
12
- * Groups multiple state updates to trigger effects only once at the end.
13
- */
14
4
  export const batch = (fn) => StateImpl.executeBatch(fn);
15
- /**
16
- * Creates a read-only computed value that updates when its dependencies change.
17
- */
18
5
  export const derive = (computeFn) => StateImpl.createDerive(computeFn);
19
- /**
20
- * Creates an efficient subscription to a subset of a state value.
21
- */
22
6
  export const select = (source, selectorFn, equalityFn = Object.is) => StateImpl.createSelect(source, selectorFn, equalityFn);
23
- /**
24
- * Creates a read-only view of a state, hiding mutation methods.
25
- */
26
7
  export const readonlyState = (state) => () => state();
27
- /**
28
- * Creates a state with access control, returning a tuple of reader and writer.
29
- */
30
8
  export const protectedState = (initialValue, equalityFn = Object.is) => {
31
9
  const fullState = state(initialValue, equalityFn);
32
10
  return [
@@ -37,26 +15,18 @@ export const protectedState = (initialValue, equalityFn = Object.is) => {
37
15
  },
38
16
  ];
39
17
  };
40
- /**
41
- * Creates a lens for direct updates to nested properties of a state.
42
- */
43
18
  export const lens = (source, accessor) => StateImpl.createLens(source, accessor);
44
19
  class StateImpl {
45
- // Static fields track global reactivity state - this centralized approach allows
46
- // for coordinated updates while maintaining individual state isolation
47
20
  static currentSubscriber = null;
48
21
  static pendingSubscribers = new Set();
49
22
  static isNotifying = false;
50
23
  static batchDepth = 0;
51
24
  static deferredEffectCreations = [];
52
25
  static activeSubscribers = new Set();
53
- // WeakMaps enable automatic garbage collection when subscribers are no
54
- // longer referenced, preventing memory leaks in long-running applications
55
26
  static stateTracking = new WeakMap();
56
27
  static subscriberDependencies = new WeakMap();
57
28
  static parentSubscriber = new WeakMap();
58
29
  static childSubscribers = new WeakMap();
59
- // Instance state - each state has unique subscribers and ID
60
30
  value;
61
31
  subscribers = new Set();
62
32
  stateId = Symbol();
@@ -65,10 +35,6 @@ class StateImpl {
65
35
  this.value = initialValue;
66
36
  this.equalityFn = equalityFn;
67
37
  }
68
- /**
69
- * Creates a reactive state container with the provided initial value.
70
- * Implementation of the public 'state' function.
71
- */
72
38
  static createState = (initialValue, equalityFn = Object.is) => {
73
39
  const instance = new StateImpl(initialValue, equalityFn);
74
40
  const get = () => instance.get();
@@ -77,23 +43,16 @@ class StateImpl {
77
43
  get[STATE_ID] = instance.stateId;
78
44
  return get;
79
45
  };
80
- // Auto-tracks dependencies when called within effects, creating a fine-grained
81
- // reactivity graph that only updates affected components
82
46
  get = () => {
83
47
  const currentEffect = StateImpl.currentSubscriber;
84
48
  if (currentEffect) {
85
- // Add this effect to subscribers for future notification
86
49
  this.subscribers.add(currentEffect);
87
- // Maintain bidirectional dependency tracking to enable precise cleanup
88
- // when effects are unsubscribed, preventing memory leaks
89
50
  let dependencies = StateImpl.subscriberDependencies.get(currentEffect);
90
51
  if (!dependencies) {
91
52
  dependencies = new Set();
92
53
  StateImpl.subscriberDependencies.set(currentEffect, dependencies);
93
54
  }
94
55
  dependencies.add(this.subscribers);
95
- // Track read states to detect direct cyclical dependencies that
96
- // could cause infinite loops
97
56
  let readStates = StateImpl.stateTracking.get(currentEffect);
98
57
  if (!readStates) {
99
58
  readStates = new Set();
@@ -103,14 +62,10 @@ class StateImpl {
103
62
  }
104
63
  return this.value;
105
64
  };
106
- // Handles value updates with built-in optimizations and safeguards
107
65
  set = (newValue) => {
108
- // Skip updates for unchanged values to prevent redundant effect executions
109
66
  if (this.equalityFn(this.value, newValue)) {
110
67
  return;
111
68
  }
112
- // Infinite loop detection prevents direct self-mutation within effects,
113
- // while allowing nested effect patterns that would otherwise appear cyclical
114
69
  const effect = StateImpl.currentSubscriber;
115
70
  if (effect) {
116
71
  const states = StateImpl.stateTracking.get(effect);
@@ -119,16 +74,12 @@ class StateImpl {
119
74
  }
120
75
  }
121
76
  this.value = newValue;
122
- // Skip updates when there are no subscribers, avoiding unnecessary processing
123
77
  if (this.subscribers.size === 0) {
124
78
  return;
125
79
  }
126
- // Queue notifications instead of executing immediately to support batch operations
127
- // and prevent redundant effect runs
128
80
  for (const sub of this.subscribers) {
129
81
  StateImpl.pendingSubscribers.add(sub);
130
82
  }
131
- // Immediate execution outside of batches, deferred execution inside batches
132
83
  if (StateImpl.batchDepth === 0 && !StateImpl.isNotifying) {
133
84
  StateImpl.notifySubscribers();
134
85
  }
@@ -136,27 +87,17 @@ class StateImpl {
136
87
  update = (fn) => {
137
88
  this.set(fn(this.value));
138
89
  };
139
- /**
140
- * Registers a function to run whenever its reactive dependencies change.
141
- * Implementation of the public 'effect' function.
142
- */
143
90
  static createEffect = (fn) => {
144
91
  const runEffect = () => {
145
- // Prevent re-entrance to avoid cascade updates during effect execution
146
92
  if (StateImpl.activeSubscribers.has(runEffect)) {
147
93
  return;
148
94
  }
149
95
  StateImpl.activeSubscribers.add(runEffect);
150
96
  const parentEffect = StateImpl.currentSubscriber;
151
97
  try {
152
- // Clean existing subscriptions before running to ensure only
153
- // currently accessed states are tracked as dependencies
154
98
  StateImpl.cleanupEffect(runEffect);
155
- // Set current context for automatic dependency tracking
156
99
  StateImpl.currentSubscriber = runEffect;
157
100
  StateImpl.stateTracking.set(runEffect, new Set());
158
- // Track parent-child relationships to handle nested effects correctly
159
- // and enable hierarchical cleanup later
160
101
  if (parentEffect) {
161
102
  StateImpl.parentSubscriber.set(runEffect, parentEffect);
162
103
  let children = StateImpl.childSubscribers.get(parentEffect);
@@ -166,22 +107,17 @@ class StateImpl {
166
107
  }
167
108
  children.add(runEffect);
168
109
  }
169
- // Execute the effect function, which will auto-track dependencies
170
110
  fn();
171
111
  }
172
112
  finally {
173
- // Restore previous context when done
174
113
  StateImpl.currentSubscriber = parentEffect;
175
114
  StateImpl.activeSubscribers.delete(runEffect);
176
115
  }
177
116
  };
178
- // Run immediately unless we're in a batch operation
179
117
  if (StateImpl.batchDepth === 0) {
180
118
  runEffect();
181
119
  }
182
120
  else {
183
- // Still track parent-child relationship even when deferred,
184
- // ensuring proper hierarchical cleanup later
185
121
  if (StateImpl.currentSubscriber) {
186
122
  const parent = StateImpl.currentSubscriber;
187
123
  StateImpl.parentSubscriber.set(runEffect, parent);
@@ -192,17 +128,13 @@ class StateImpl {
192
128
  }
193
129
  children.add(runEffect);
194
130
  }
195
- // Queue for execution when batch completes
196
131
  StateImpl.deferredEffectCreations.push(runEffect);
197
132
  }
198
- // Return cleanup function to properly disconnect from reactivity graph
199
133
  return () => {
200
- // Remove from dependency tracking to stop future notifications
201
134
  StateImpl.cleanupEffect(runEffect);
202
135
  StateImpl.pendingSubscribers.delete(runEffect);
203
136
  StateImpl.activeSubscribers.delete(runEffect);
204
137
  StateImpl.stateTracking.delete(runEffect);
205
- // Clean up parent-child relationship bidirectionally
206
138
  const parent = StateImpl.parentSubscriber.get(runEffect);
207
139
  if (parent) {
208
140
  const siblings = StateImpl.childSubscribers.get(parent);
@@ -211,8 +143,6 @@ class StateImpl {
211
143
  }
212
144
  }
213
145
  StateImpl.parentSubscriber.delete(runEffect);
214
- // Recursively clean up child effects to prevent memory leaks in
215
- // nested effect scenarios
216
146
  const children = StateImpl.childSubscribers.get(runEffect);
217
147
  if (children) {
218
148
  for (const child of children) {
@@ -223,19 +153,12 @@ class StateImpl {
223
153
  }
224
154
  };
225
155
  };
226
- /**
227
- * Groups multiple state updates to trigger effects only once at the end.
228
- * Implementation of the public 'batch' function.
229
- */
230
156
  static executeBatch = (fn) => {
231
- // Increment depth counter to handle nested batches correctly
232
157
  StateImpl.batchDepth++;
233
158
  try {
234
159
  return fn();
235
160
  }
236
161
  catch (error) {
237
- // Clean up on error to prevent stale subscribers from executing
238
- // and potentially causing cascading errors
239
162
  if (StateImpl.batchDepth === 1) {
240
163
  StateImpl.pendingSubscribers.clear();
241
164
  StateImpl.deferredEffectCreations.length = 0;
@@ -244,10 +167,7 @@ class StateImpl {
244
167
  }
245
168
  finally {
246
169
  StateImpl.batchDepth--;
247
- // Only process effects when exiting the outermost batch,
248
- // maintaining proper execution order while avoiding redundant runs
249
170
  if (StateImpl.batchDepth === 0) {
250
- // Process effects created during the batch
251
171
  if (StateImpl.deferredEffectCreations.length > 0) {
252
172
  const effectsToRun = [...StateImpl.deferredEffectCreations];
253
173
  StateImpl.deferredEffectCreations.length = 0;
@@ -255,34 +175,24 @@ class StateImpl {
255
175
  effect();
256
176
  }
257
177
  }
258
- // Process state updates that occurred during the batch
259
178
  if (StateImpl.pendingSubscribers.size > 0 && !StateImpl.isNotifying) {
260
179
  StateImpl.notifySubscribers();
261
180
  }
262
181
  }
263
182
  }
264
183
  };
265
- /**
266
- * Creates a read-only computed value that updates when its dependencies change.
267
- * Implementation of the public 'derive' function.
268
- */
269
184
  static createDerive = (computeFn) => {
270
185
  const valueState = StateImpl.createState(undefined);
271
186
  let initialized = false;
272
187
  let cachedValue;
273
- // Internal effect automatically tracks dependencies and updates the derived value
274
188
  StateImpl.createEffect(() => {
275
189
  const newValue = computeFn();
276
- // Only update if the value actually changed to preserve referential equality
277
- // and prevent unnecessary downstream updates
278
190
  if (!(initialized && Object.is(cachedValue, newValue))) {
279
191
  cachedValue = newValue;
280
192
  valueState.set(newValue);
281
193
  }
282
194
  initialized = true;
283
195
  });
284
- // Return function with lazy initialization - ensures value is available
285
- // even when accessed before its dependencies have had a chance to update
286
196
  return () => {
287
197
  if (!initialized) {
288
198
  cachedValue = computeFn();
@@ -292,35 +202,25 @@ class StateImpl {
292
202
  return valueState();
293
203
  };
294
204
  };
295
- /**
296
- * Creates an efficient subscription to a subset of a state value.
297
- * Implementation of the public 'select' function.
298
- */
299
205
  static createSelect = (source, selectorFn, equalityFn = Object.is) => {
300
206
  let lastSourceValue;
301
207
  let lastSelectedValue;
302
208
  let initialized = false;
303
209
  const valueState = StateImpl.createState(undefined);
304
- // Internal effect to track the source and update only when needed
305
210
  StateImpl.createEffect(() => {
306
211
  const sourceValue = source();
307
- // Skip computation if source reference hasn't changed
308
212
  if (initialized && Object.is(lastSourceValue, sourceValue)) {
309
213
  return;
310
214
  }
311
215
  lastSourceValue = sourceValue;
312
216
  const newSelectedValue = selectorFn(sourceValue);
313
- // Use custom equality function to determine if value semantically changed,
314
- // allowing for deep equality comparisons with complex objects
315
217
  if (initialized && lastSelectedValue !== undefined && equalityFn(lastSelectedValue, newSelectedValue)) {
316
218
  return;
317
219
  }
318
- // Update cache and notify subscribers due the value has changed
319
220
  lastSelectedValue = newSelectedValue;
320
221
  valueState.set(newSelectedValue);
321
222
  initialized = true;
322
223
  });
323
- // Return function with eager initialization capability
324
224
  return () => {
325
225
  if (!initialized) {
326
226
  lastSourceValue = source();
@@ -331,12 +231,7 @@ class StateImpl {
331
231
  return valueState();
332
232
  };
333
233
  };
334
- /**
335
- * Creates a lens for direct updates to nested properties of a state.
336
- * Implementation of the public 'lens' function.
337
- */
338
234
  static createLens = (source, accessor) => {
339
- // Extract the property path once during lens creation
340
235
  const extractPath = () => {
341
236
  const path = [];
342
237
  const proxy = new Proxy({}, {
@@ -351,17 +246,12 @@ class StateImpl {
351
246
  accessor(proxy);
352
247
  }
353
248
  catch {
354
- // Ignore errors, we're just collecting the path
355
249
  }
356
250
  return path;
357
251
  };
358
- // Capture the path once
359
252
  const path = extractPath();
360
- // Create a state with the initial value from the source
361
253
  const lensState = StateImpl.createState(accessor(source()));
362
- // Prevent circular updates
363
254
  let isUpdating = false;
364
- // Set up an effect to sync from source to lens
365
255
  StateImpl.createEffect(() => {
366
256
  if (isUpdating) {
367
257
  return;
@@ -374,7 +264,6 @@ class StateImpl {
374
264
  isUpdating = false;
375
265
  }
376
266
  });
377
- // Override the lens state's set method to update the source
378
267
  const originalSet = lensState.set;
379
268
  lensState.set = (value) => {
380
269
  if (isUpdating) {
@@ -382,35 +271,25 @@ class StateImpl {
382
271
  }
383
272
  isUpdating = true;
384
273
  try {
385
- // Update lens state
386
274
  originalSet(value);
387
- // Update source by modifying the value at path
388
275
  source.update((current) => setValueAtPath(current, path, value));
389
276
  }
390
277
  finally {
391
278
  isUpdating = false;
392
279
  }
393
280
  };
394
- // Add update method for completeness
395
281
  lensState.update = (fn) => {
396
282
  lensState.set(fn(lensState()));
397
283
  };
398
284
  return lensState;
399
285
  };
400
- // Processes queued subscriber notifications in a controlled, non-reentrant way
401
286
  static notifySubscribers = () => {
402
- // Prevent reentrance to avoid cascading notification loops when
403
- // effects trigger further state changes
404
287
  if (StateImpl.isNotifying) {
405
288
  return;
406
289
  }
407
290
  StateImpl.isNotifying = true;
408
291
  try {
409
- // Process all pending effects in batches for better perf,
410
- // ensuring topological execution order is maintained
411
292
  while (StateImpl.pendingSubscribers.size > 0) {
412
- // Process in snapshot batches to prevent infinite loops
413
- // when effects trigger further state changes
414
293
  const subscribers = Array.from(StateImpl.pendingSubscribers);
415
294
  StateImpl.pendingSubscribers.clear();
416
295
  for (const effect of subscribers) {
@@ -422,11 +301,8 @@ class StateImpl {
422
301
  StateImpl.isNotifying = false;
423
302
  }
424
303
  };
425
- // Removes effect from dependency tracking to prevent memory leaks
426
304
  static cleanupEffect = (effect) => {
427
- // Remove from execution queue to prevent stale updates
428
305
  StateImpl.pendingSubscribers.delete(effect);
429
- // Remove bidirectional dependency references to prevent memory leaks
430
306
  const deps = StateImpl.subscriberDependencies.get(effect);
431
307
  if (deps) {
432
308
  for (const subscribers of deps) {
@@ -437,72 +313,54 @@ class StateImpl {
437
313
  }
438
314
  };
439
315
  }
440
- // Helper for array updates
441
316
  const updateArrayItem = (arr, index, value) => {
442
317
  const copy = [...arr];
443
318
  copy[index] = value;
444
319
  return copy;
445
320
  };
446
- // Helper for single-level updates (optimization)
447
321
  const updateShallowProperty = (obj, key, value) => {
448
322
  const result = { ...obj };
449
323
  result[key] = value;
450
324
  return result;
451
325
  };
452
- // Helper to create the appropriate container type
453
326
  const createContainer = (key) => {
454
327
  const isArrayKey = typeof key === 'number' || !Number.isNaN(Number(key));
455
328
  return isArrayKey ? [] : {};
456
329
  };
457
- // Helper for handling array path updates
458
330
  const updateArrayPath = (array, pathSegments, value) => {
459
331
  const index = Number(pathSegments[0]);
460
332
  if (pathSegments.length === 1) {
461
- // Simple array item update
462
333
  return updateArrayItem(array, index, value);
463
334
  }
464
- // Nested path in array
465
335
  const copy = [...array];
466
336
  const nextPathSegments = pathSegments.slice(1);
467
337
  const nextKey = nextPathSegments[0];
468
- // For null/undefined values in arrays, create appropriate containers
469
338
  let nextValue = array[index];
470
339
  if (nextValue === undefined || nextValue === null) {
471
- // Use empty object as default if nextKey is undefined
472
340
  nextValue = nextKey !== undefined ? createContainer(nextKey) : {};
473
341
  }
474
342
  copy[index] = setValueAtPath(nextValue, nextPathSegments, value);
475
343
  return copy;
476
344
  };
477
- // Helper for handling object path updates
478
345
  const updateObjectPath = (obj, pathSegments, value) => {
479
- // Ensure we have a valid key
480
346
  const currentKey = pathSegments[0];
481
347
  if (currentKey === undefined) {
482
- // This shouldn't happen given our checks in the main function
483
348
  return obj;
484
349
  }
485
350
  if (pathSegments.length === 1) {
486
- // Simple object property update
487
351
  return updateShallowProperty(obj, currentKey, value);
488
352
  }
489
- // Nested path in object
490
353
  const nextPathSegments = pathSegments.slice(1);
491
354
  const nextKey = nextPathSegments[0];
492
- // For null/undefined values, create appropriate containers
493
355
  let currentValue = obj[currentKey];
494
356
  if (currentValue === undefined || currentValue === null) {
495
- // Use empty object as default if nextKey is undefined
496
357
  currentValue = nextKey !== undefined ? createContainer(nextKey) : {};
497
358
  }
498
- // Create new object with updated property
499
359
  const result = { ...obj };
500
360
  result[currentKey] = setValueAtPath(currentValue, nextPathSegments, value);
501
361
  return result;
502
362
  };
503
- // Simplified function to update a nested value at a path
504
363
  const setValueAtPath = (obj, pathSegments, value) => {
505
- // Handle base cases
506
364
  if (pathSegments.length === 0) {
507
365
  return value;
508
366
  }
@@ -513,7 +371,6 @@ const setValueAtPath = (obj, pathSegments, value) => {
513
371
  if (currentKey === undefined) {
514
372
  return obj;
515
373
  }
516
- // Delegate to specialized handlers based on data type
517
374
  if (Array.isArray(obj)) {
518
375
  return updateArrayPath(obj, pathSegments, value);
519
376
  }
@@ -0,0 +1 @@
1
+ let s=Symbol(),i=(e,t=Object.is)=>u.createState(e,t);var e=e=>u.createEffect(e),t=e=>u.executeBatch(e),r=e=>u.createDerive(e),c=(e,t,r=Object.is)=>u.createSelect(e,t,r);let a=e=>()=>e();var n=(e,t=Object.is)=>{let r=i(e,t);return[()=>a(r)(),{set:e=>r.set(e),update:e=>r.update(e)}]},b=(e,t)=>u.createLens(e,t);class u{static currentSubscriber=null;static pendingSubscribers=new Set;static isNotifying=!1;static batchDepth=0;static deferredEffectCreations=[];static activeSubscribers=new Set;static stateTracking=new WeakMap;static subscriberDependencies=new WeakMap;static parentSubscriber=new WeakMap;static childSubscribers=new WeakMap;value;subscribers=new Set;stateId=Symbol();equalityFn;constructor(e,t=Object.is){this.value=e,this.equalityFn=t}static createState=(e,t=Object.is)=>{let r=new u(e,t);e=()=>r.get();return e.set=e=>r.set(e),e.update=e=>r.update(e),e[s]=r.stateId,e};get=()=>{var r=u.currentSubscriber;if(r){this.subscribers.add(r);let e=u.subscriberDependencies.get(r),t=(e||(e=new Set,u.subscriberDependencies.set(r,e)),e.add(this.subscribers),u.stateTracking.get(r));t||(t=new Set,u.stateTracking.set(r,t)),t.add(this.stateId)}return this.value};set=e=>{if(!this.equalityFn(this.value,e)){var t=u.currentSubscriber;if(t)if(u.stateTracking.get(t)?.has(this.stateId)&&!u.parentSubscriber.get(t))throw new Error("Infinite loop detected: effect() cannot update a state() it depends on!");if(this.value=e,0!==this.subscribers.size){for(var r of this.subscribers)u.pendingSubscribers.add(r);0!==u.batchDepth||u.isNotifying||u.notifySubscribers()}}};update=e=>{this.set(e(this.value))};static createEffect=e=>{let r=()=>{if(!u.activeSubscribers.has(r)){u.activeSubscribers.add(r);var t=u.currentSubscriber;try{if(u.cleanupEffect(r),u.currentSubscriber=r,u.stateTracking.set(r,new Set),t){u.parentSubscriber.set(r,t);let e=u.childSubscribers.get(t);e||(e=new Set,u.childSubscribers.set(t,e)),e.add(r)}e()}finally{u.currentSubscriber=t,u.activeSubscribers.delete(r)}}};if(0===u.batchDepth)r();else{if(u.currentSubscriber){var t=u.currentSubscriber;u.parentSubscriber.set(r,t);let e=u.childSubscribers.get(t);e||(e=new Set,u.childSubscribers.set(t,e)),e.add(r)}u.deferredEffectCreations.push(r)}return()=>{u.cleanupEffect(r),u.pendingSubscribers.delete(r),u.activeSubscribers.delete(r),u.stateTracking.delete(r);var e=u.parentSubscriber.get(r),e=(e&&(e=u.childSubscribers.get(e))&&e.delete(r),u.parentSubscriber.delete(r),u.childSubscribers.get(r));if(e){for(var t of e)u.cleanupEffect(t);e.clear(),u.childSubscribers.delete(r)}}};static executeBatch=e=>{u.batchDepth++;try{return e()}catch(e){throw 1===u.batchDepth&&(u.pendingSubscribers.clear(),u.deferredEffectCreations.length=0),e}finally{if(u.batchDepth--,0===u.batchDepth){if(0<u.deferredEffectCreations.length){var t,e=[...u.deferredEffectCreations];u.deferredEffectCreations.length=0;for(t of e)t()}0<u.pendingSubscribers.size&&!u.isNotifying&&u.notifySubscribers()}}};static createDerive=t=>{let r=u.createState(void 0),s=!1,i;return u.createEffect(()=>{var e=t();s&&Object.is(i,e)||(i=e,r.set(e)),s=!0}),()=>(s||(i=t(),s=!0,r.set(i)),r())};static createSelect=(t,r,s=Object.is)=>{let i,c,a=!1,n=u.createState(void 0);return u.createEffect(()=>{var e=t();a&&Object.is(i,e)||(i=e,e=r(e),a&&void 0!==c&&s(c,e))||(c=e,n.set(e),a=!0)}),()=>(a||(i=t(),c=r(i),n.set(c),a=!0),n())};static createLens=(e,t)=>{let r=(()=>{let r=[],s=new Proxy({},{get:(e,t)=>("string"!=typeof t&&"number"!=typeof t||r.push(t),s)});try{t(s)}catch{}return r})(),s=u.createState(t(e())),i=!1,c=(u.createEffect(()=>{if(!i){i=!0;try{s.set(t(e()))}finally{i=!1}}}),s.set);return s.set=t=>{if(!i){i=!0;try{c(t),e.update(e=>p(e,r,t))}finally{i=!1}}},s.update=e=>{s.set(e(s()))},s};static notifySubscribers=()=>{if(!u.isNotifying){u.isNotifying=!0;try{for(;0<u.pendingSubscribers.size;){var e,t=Array.from(u.pendingSubscribers);u.pendingSubscribers.clear();for(e of t)e()}}finally{u.isNotifying=!1}}};static cleanupEffect=e=>{u.pendingSubscribers.delete(e);var t=u.subscriberDependencies.get(e);if(t){for(var r of t)r.delete(e);t.clear(),u.subscriberDependencies.delete(e)}}}let f=(e,t,r)=>{e=[...e];return e[t]=r,e},l=(e,t,r)=>{e={...e};return e[t]=r,e},d=e=>"number"==typeof e||!Number.isNaN(Number(e))?[]:{},S=(e,t,r)=>{var s=Number(t[0]);if(1===t.length)return f(e,s,r);var i=[...e],t=t.slice(1),c=t[0];let a=e[s];return null==a&&(a=void 0!==c?d(c):{}),i[s]=p(a,t,r),i},h=(e,t,r)=>{var s=t[0];if(void 0===s)return e;if(1===t.length)return l(e,s,r);var t=t.slice(1),i=t[0];let c=e[s];null==c&&(c=void 0!==i?d(i):{});i={...e};return i[s]=p(c,t,r),i},p=(e,t,r)=>0===t.length?r:null==e?p({},t,r):void 0===t[0]?e:(Array.isArray(e)?S:h)(e,t,r);export{i as state,e as effect,t as batch,r as derive,c as select,a as readonlyState,n as protectedState,b as lens};
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@nerdalytics/beacon",
3
- "version": "1000.2.0",
3
+ "version": "1000.2.1",
4
4
  "description": "A lightweight reactive state library for Node.js backends. Enables reactive state management with automatic dependency tracking and efficient updates for server-side applications.",
5
5
  "type": "module",
6
- "main": "dist/src/index.js",
6
+ "main": "dist/src/index.min.js",
7
7
  "types": "dist/src/index.d.ts",
8
8
  "files": ["dist/src/index.js", "dist/src/index.d.ts", "src/index.ts", "LICENSE"],
9
+ "exports": {
10
+ ".": {
11
+ "typescript": "./src/index.ts",
12
+ "default": "./dist/src/index.js"
13
+ }
14
+ },
9
15
  "repository": {
10
16
  "url": "git+https://github.com/nerdalytics/beacon.git",
11
17
  "type": "git"
@@ -34,6 +40,7 @@
34
40
  "build": "npm run build:lts",
35
41
  "prebuild:lts": "rm -rf dist/",
36
42
  "build:lts": "tsc -p tsconfig.lts.json",
43
+ "postbuild:lts": "npx uglify-js --compress --mangle --module --toplevel --v8 --warn --source-map \"content='dist/src/index.js.map'\" --output dist/src/index.min.js dist/src/index.js",
37
44
  "prepublishOnly": "npm run build:lts",
38
45
  "pretest:lts": "node scripts/run-lts-tests.js",
39
46
  "test:lts:20": "node --test dist/tests/**.js",
@@ -63,7 +70,8 @@
63
70
  "devDependencies": {
64
71
  "@biomejs/biome": "1.9.4",
65
72
  "@types/node": "22.14.1",
66
- "typescript": "5.8.3"
73
+ "typescript": "5.8.3",
74
+ "uglify-js": "3.19.3"
67
75
  },
68
76
  "engines": {
69
77
  "node": ">=20.0.0"