@nerdalytics/beacon 1000.3.1 → 1000.3.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Beacon <img align="right" src="https://raw.githubusercontent.com/nerdalytics/beacon/refs/heads/trunk/assets/beacon-logo-v2.svg" width="128px" alt="A stylized lighthouse beacon with golden light against a dark blue background, representing the reactive state library"/>
2
2
 
3
- > Lightweight reactive state management for Node.js backends
3
+ > Reactive dependency graph runtime for Node.js backends. Tracks dependencies between signals and propagates updates automatically.
4
4
 
5
5
  [![license:mit](https://flat.badgen.net/static/license/MIT/blue)](https://github.com/nerdalytics/beacon/blob/trunk/LICENSE)
6
6
  [![registry:npm:version](https://img.shields.io/npm/v/@nerdalytics/beacon.svg)](https://www.npmjs.com/package/@nerdalytics/beacon)
@@ -10,8 +10,6 @@
10
10
  [![language:typescript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)](https://typescriptlang.org/)
11
11
  [![linter:biome](https://img.shields.io/badge/biome-60a5fa?style=for-the-badge&logo=biome&logoColor=white)](https://biomejs.dev/)
12
12
 
13
- A lightweight reactive state library for Node.js backends. Enables reactive state management with automatic dependency tracking and efficient updates for server-side applications.
14
-
15
13
  ## Installation
16
14
 
17
15
  ```
@@ -39,6 +37,16 @@ count.set(5);
39
37
  Full documentation, API reference, and examples available at:
40
38
  **[nerdalytics.github.io/beacon](https://nerdalytics.github.io/beacon/)**
41
39
 
40
+ ### LLM-friendly docs
41
+
42
+ The handbook has plain-text endpoints for LLMs:
43
+
44
+ - [`llms.txt`](https://nerdalytics.github.io/beacon/llms.txt) lists available versions
45
+ - [`<version>/llms.txt`](https://nerdalytics.github.io/beacon/latest/llms.txt) lists pages for a version
46
+ - [`<version>/llms-full.txt`](https://nerdalytics.github.io/beacon/latest/llms-full.txt) concatenates every page into one file
47
+
48
+ You can also append `.md` to any handbook page URL for its Markdown source (e.g. [`<version>/introduction.md`](https://nerdalytics.github.io/beacon/latest/introduction.md)).
49
+
42
50
  ## License
43
51
 
44
52
  MIT - See [LICENSE](./LICENSE) for details.
@@ -1 +1 @@
1
- let r=Symbol("STATE_ID"),f=null,u=new Set,s=!1,d=0,a=[],l=new Set,o=new WeakMap,v=new WeakMap,c=new WeakMap,i=new WeakMap,y=(e,t,r)=>{let n=e.get(t);return n||(n=r(),e.set(t,n)),n},p=()=>{if(!s){s=!0;try{for(;0<u.size;){var e,t=u;u=new Set;for(e of t)e()}}finally{s=!1}}},w=e=>{u.delete(e);var t=v.get(e);if(t){for(var r of t)r.delete(e);t.clear(),v.delete(e)}},g=e=>{w(e),l.delete(e),o.delete(e);var t=c.get(e),t=(t&&(t=i.get(t))&&t.delete(e),c.delete(e),i.get(e));if(t){for(var r of t)g(r);t.clear(),i.delete(e)}},h=(e,n=Object.is)=>{let a=e,l=new Set,i=Symbol(),t=()=>{var e=f;return e&&(l.add(e),y(v,e,()=>new Set).add(l),y(o,e,()=>new Set).add(i)),a};return t.set=e=>{if(!n(a,e)){var t=f;if(t)if(o.get(t)?.has(i)&&!c.get(t))throw new Error("Infinite loop detected: effect() cannot update a state() it depends on!");if(a=e,0!==l.size){for(var r of l)u.add(r);0!==d||s||p()}}},t.update=e=>{t.set(e(a))},t[r]=i,t},S=r=>{let n=()=>{if(!l.has(n)){l.add(n);var e=f;try{w(n),f=n;var t=o.get(n);t?t.clear():o.set(n,new Set),e&&(c.set(n,e),y(i,e,()=>new Set).add(n)),r()}finally{f=e,l.delete(n)}}};var e;return 0===d?n():(f&&(e=f,c.set(n,e),y(i,e,()=>new Set).add(n)),a.push(n)),()=>{g(n)}};var e=e=>{d++;try{return e()}catch(e){throw 1===d&&(u.clear(),a.length=0),e}finally{if(0===--d){if(0<a.length){var t,e=a;a=[];for(t of e)t()}0<u.size&&!s&&p()}}},t=t=>{let r=void 0,n=!1,a=h(void 0);return S(function(){var e=t();n&&Object.is(r,e)||(r=e,a.set(e)),n=!0}),function(){return n||(r=t(),n=!0,a.set(r)),a()}},n=(t,r,n=Object.is)=>{let a=!1,l,i,f=h(void 0);return S(function(){var e=t();a&&Object.is(i,e)||(i=e,e=r(e),a&&void 0!==l&&n(l,e))||(l=e,f.set(e),a=!0)}),function(){return a||(i=t(),l=r(i),f.set(l),a=!0),f()}};let b=(e,t,r)=>{e=[...e];return e[t]=r,e},m=(e,t,r)=>{e={...e};return e[t]=r,e},j=e=>"number"==typeof e||!Number.isNaN(Number(e))?[]:{},N=(n,a,l,i)=>{if(l>=a.length)return i;if(null==n)return N({},a,l,i);var f=a[l];if(void 0===f)return n;if(Array.isArray(n)){var u=Number(f);if(l===a.length-1)return b(n,u,i);var s=[...n];let e=l+1,t=a[e],r=n[u];return null==r&&(r=void 0===t?{}:j(t)),s[u]=N(r,a,e,i),s}u=n;if(l===a.length-1)return m(u,f,i);let e=l+1,t=a[e],r=u[f];null==r&&(r=void 0===t?{}:j(t));s={...u};return s[f]=N(r,a,e,i),s};var O=(e,t)=>{let r=!1;let n=(()=>{let r=[],n=new Proxy({},{get:(e,t)=>("string"!=typeof t&&"number"!=typeof t||r.push(t),n)});try{t(n)}catch{}return r})(),a=h(t(e())),l=a.set;return S(function(){if(!r){r=!0;try{a.set(t(e()))}finally{r=!1}}}),a.set=function(t){if(!r){r=!0;try{l(t),e.update(e=>N(e,n,0,t))}finally{r=!1}}},a.update=function(e){a.set(e(a()))},a};let k=e=>()=>e();var M=(e,t=Object.is)=>{let r=h(e,t);return[k(r),{set:e=>r.set(e),update:e=>r.update(e)}]};export{t as derive,S as effect,O as lens,M as protectedState,k as readonlyState,n as select,h as state,e as batch};
1
+ let r=Symbol("STATE_ID"),f=null,o=new Set,s=!1,u=0,a=[],l=new Set,d=new WeakMap,c=new WeakMap,v=new WeakMap,i=new WeakMap,p=new Set(["__proto__","constructor","prototype"]),y=(e,t,r)=>{let n=e.get(t);return n||(n=r(),e.set(t,n)),n},w=()=>{if(!s){s=!0;try{for(;0<o.size;){var e,t=o;o=new Set;for(e of t)e()}}finally{s=!1}}},g=e=>{o.delete(e);var t=c.get(e);if(t){for(var r of t)r.delete(e);t.clear(),c.delete(e)}},h=e=>{g(e),l.delete(e),d.delete(e);var t=v.get(e),t=(t&&(t=i.get(t))&&t.delete(e),v.delete(e),i.get(e));if(t){for(var r of t)h(r);t.clear(),i.delete(e)}},S=(e,n=Object.is)=>{let a=e,l=new Set,i=Symbol(),t=()=>{var e=f;return e&&(l.add(e),y(c,e,()=>new Set).add(l),y(d,e,()=>new Set).add(i)),a};return t.set=e=>{if(!n(a,e)){var t=f;if(t)if(d.get(t)?.has(i)&&!v.get(t))throw new Error("Infinite loop detected: effect() cannot update a state() it depends on!");if(a=e,0!==l.size){for(var r of l)o.add(r);0!==u||s||w()}}},t.update=e=>{t.set(e(a))},t[r]=i,t},b=r=>{let n=()=>{if(!l.has(n)){l.add(n);var e=f;try{g(n),f=n;var t=d.get(n);t?t.clear():d.set(n,new Set),e&&(v.set(n,e),y(i,e,()=>new Set).add(n)),r()}finally{f=e,l.delete(n)}}};var e;return 0===u?n():(f&&(e=f,v.set(n,e),y(i,e,()=>new Set).add(n)),a.push(n)),()=>{h(n)}};var e=e=>{u++;try{return e()}catch(e){throw 1===u&&(o.clear(),a.length=0),e}finally{if(0===--u){if(0<a.length){var t,e=a;a=[];for(t of e)t()}0<o.size&&!s&&w()}}},t=t=>{let r=void 0,n=!1,a=S(void 0);return b(function(){var e=t();n&&Object.is(r,e)||(r=e,a.set(e)),n=!0}),function(){return n||(r=t(),n=!0,a.set(r)),a()}},n=(t,r,n=Object.is)=>{let a=!1,l,i,f=S(void 0);return b(function(){var e=t();a&&Object.is(i,e)||(i=e,e=r(e),a&&void 0!==l&&n(l,e))||(l=e,f.set(e),a=!0)}),function(){return a||(i=t(),l=r(i),f.set(l),a=!0),f()}};let m=(e,t,r)=>{e=[...e];return e[t]=r,e},j=(e,t,r)=>{e={...e};return e[t]=r,e},N=e=>"number"==typeof e||!Number.isNaN(Number(e))?[]:{},O=(n,a,l,i)=>{if(l>=a.length)return i;if(null==n)return O({},a,l,i);var f=a[l];if(void 0===f)return n;if(Array.isArray(n)){var o=Number(f);if(l===a.length-1)return m(n,o,i);var s=[...n];let e=l+1,t=a[e],r=n[o];return null==r&&(r=void 0===t?{}:N(t)),s[o]=O(r,a,e,i),s}o=n;if(l===a.length-1)return j(o,f,i);let e=l+1,t=a[e],r=o[f];null==r&&(r=void 0===t?{}:N(t));s={...o};return s[f]=O(r,a,e,i),s};var _=(e,t)=>{let r=!1;let n=(()=>{let r=[],n=!1,a=new Proxy({},{get:(e,t)=>(n||"string"!=typeof t&&"number"!=typeof t||(p.has(String(t))?n=!0:r.push(t)),a)});try{t(a)}catch{}return n?[]:r})(),a=S(t(e())),l=a.set;return b(function(){if(!r){r=!0;try{a.set(t(e()))}finally{r=!1}}}),a.set=function(t){if(!r&&0!==n.length){r=!0;try{l(t),e.update(e=>O(e,n,0,t))}finally{r=!1}}},a.update=function(e){a.set(e(a()))},a};let k=e=>()=>e();var M=(e,t=Object.is)=>{let r=S(e,t);return[k(r),{set:e=>r.set(e),update:e=>r.update(e)}]};export{t as derive,b as effect,_ as lens,M as protectedState,k as readonlyState,n as select,S as state,e as batch};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "author": "Denny Trebbin (nerdalytics)",
3
- "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.",
3
+ "description": "Reactive dependency graph runtime for Node.js backends. Tracks dependencies between signals and propagates updates automatically.",
4
4
  "devDependencies": {
5
5
  "@biomejs/biome": "2.3.14",
6
6
  "@types/node": "25.2.2",
@@ -94,5 +94,5 @@
94
94
  "sideEffects": false,
95
95
  "type": "module",
96
96
  "types": "dist/src/index.d.ts",
97
- "version": "1000.3.1"
97
+ "version": "1000.3.2"
98
98
  }
package/src/index.ts CHANGED
@@ -29,6 +29,11 @@ const subscriberDependencies: WeakMap<Subscriber, Set<Set<Subscriber>>> = new We
29
29
  >()
30
30
  const parentSubscriber: WeakMap<Subscriber, Subscriber> = new WeakMap<Subscriber, Subscriber>()
31
31
  const childSubscribers: WeakMap<Subscriber, Set<Subscriber>> = new WeakMap<Subscriber, Set<Subscriber>>()
32
+ const DANGEROUS_KEYS: ReadonlySet<string> = new Set([
33
+ '__proto__',
34
+ 'constructor',
35
+ 'prototype',
36
+ ])
32
37
 
33
38
  const getOrCreate = <K extends object, V>(map: WeakMap<K, V>, key: K, factory: () => V): V => {
34
39
  let value = map.get(key)
@@ -384,12 +389,17 @@ const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K>
384
389
 
385
390
  const extractPath = (): (string | number)[] => {
386
391
  const pathCollector: (string | number)[] = []
392
+ let tainted = false
387
393
  const proxy = new Proxy(
388
394
  {},
389
395
  {
390
396
  get: (_: object, prop: string | symbol): unknown => {
391
- if (typeof prop === 'string' || typeof prop === 'number') {
392
- pathCollector.push(prop)
397
+ if (!tainted && (typeof prop === 'string' || typeof prop === 'number')) {
398
+ if (DANGEROUS_KEYS.has(String(prop))) {
399
+ tainted = true
400
+ } else {
401
+ pathCollector.push(prop)
402
+ }
393
403
  }
394
404
  return proxy
395
405
  },
@@ -402,7 +412,7 @@ const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K>
402
412
  // Ignore errors, we're just collecting the path
403
413
  }
404
414
 
405
- return pathCollector
415
+ return tainted ? [] : pathCollector
406
416
  }
407
417
 
408
418
  const path = extractPath()
@@ -423,7 +433,7 @@ const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K>
423
433
  })
424
434
 
425
435
  lensState.set = function lensSet(value: K): void {
426
- if (isUpdating) {
436
+ if (isUpdating || path.length === 0) {
427
437
  return
428
438
  }
429
439