@uistate/renderer 1.0.1 → 1.0.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
@@ -33,9 +33,9 @@ A reactive counter. No React. No Babel. No bundler. No innerHTML. Just HTML attr
33
33
 
34
34
  ## Three Primitives
35
35
 
36
- ### 1. Delegated Actions (DOM Store)
36
+ ### 1. Delegated Actions (DOM -> Store)
37
37
 
38
- Attach store writes to user events. Three delegated listeners on the root handle everything they survive DOM mutations and never need re-wiring.
38
+ Attach store writes to user events. Three delegated listeners on the root handle everything; they survive DOM mutations and never need re-wiring.
39
39
 
40
40
  | Attribute | Event | Example |
41
41
  |---|---|---|
@@ -58,9 +58,9 @@ Attach store writes to user events. Three delegated listeners on the root handle
58
58
  | `set="flag:false"` | `store.set('flag', false)` |
59
59
  | `set="val:null"` | `store.set('val', null)` |
60
60
 
61
- ### 2. Direct Node Binding (Store DOM)
61
+ ### 2. Direct Node Binding (Store -> DOM)
62
62
 
63
- Bind store paths directly to DOM node properties. Each binding creates one EventState subscription and performs surgical updates no re-rendering, no diffing.
63
+ Bind store paths directly to DOM node properties. Each binding creates one EventState subscription and performs surgical updates; no re-rendering, no diffing.
64
64
 
65
65
  | Attribute | What it does | Example |
66
66
  |---|---|---|
@@ -124,16 +124,16 @@ const cleanup = mount(store);
124
124
  import { parseSetExpr, evalExpr, parsePush } from '@uistate/renderer';
125
125
 
126
126
  parseSetExpr('count:increment');
127
- // { path: 'count', expr: 'increment' }
127
+ // -> { path: 'count', expr: 'increment' }
128
128
 
129
129
  evalExpr('increment', 5);
130
- // 6
130
+ // -> 6
131
131
 
132
132
  parsePush('push(draft)');
133
- // { source: 'draft' }
133
+ // -> { source: 'draft' }
134
134
  ```
135
135
 
136
- These are the same functions the renderer uses internally. Because they're pure, they run in Node with zero dependencies enabling the self-test.
136
+ These are the same functions the renderer uses internally. Because they're pure, they run in Node with zero dependencies, enabling the self-test.
137
137
 
138
138
  ## Testing
139
139
 
@@ -171,11 +171,11 @@ HTML is the skeleton. The store is the brain. Bindings are the nerves.
171
171
 
172
172
  ## Philosophy
173
173
 
174
- The renderer exists to prove that **the state layer is the real product**. The same `@uistate/core` store works with React, Vue, Svelte, Angular or with this 268-line renderer that needs nothing but a browser.
174
+ The renderer exists to prove that **the state layer is the real product**. The same `@uistate/core` store works with React, Vue, Svelte, Angular, or with this 268-line renderer that needs nothing but a browser.
175
175
 
176
176
  ```
177
- A React Component (for comparison): f(props, ownState, lifecycle, hooks, context, memo, refs) VDOM DOM
178
- A UIstate Renderer (for comparison): mount(store) bind-text="path" textContent
177
+ A React Component (for comparison): f(props, ownState, lifecycle, hooks, context, memo, refs) -> VDOM -> DOM
178
+ A UIstate Renderer (for comparison): mount(store) -> bind-text="path" -> textContent
179
179
  ```
180
180
 
181
181
  ## Author
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uistate/renderer",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Direct-binding reactive renderer for @uistate/core. Zero build step.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/renderer.js CHANGED
@@ -1,18 +1,18 @@
1
1
  /**
2
- * @uistate/renderer Direct-binding reactive renderer for EventState
2
+ * @uistate/renderer: Direct-binding reactive renderer for EventState
3
3
  *
4
4
  * Copyright (c) 2025 Ajdin Imsirovic
5
5
  *
6
6
  * Three primitives:
7
- * 1. Delegated Actions (DOM Store): set, set-blur, set-enter
8
- * 2. Direct Node Binding (Store DOM): bind-text, bind-value, bind-data-*, bind-focus
7
+ * 1. Delegated Actions (DOM -> Store): set, set-blur, set-enter
8
+ * 2. Direct Node Binding (Store -> DOM): bind-text, bind-value, bind-data-*, bind-focus
9
9
  * 3. Keyed Collections: each="path" + <template>
10
10
  *
11
11
  * No templates. No innerHTML. No interpolation. No diffing.
12
12
  * Event delegation survives DOM mutations. Bindings are surgical.
13
13
  */
14
14
 
15
- // ── Pure helpers ──────────────────────────────────────────────────────
15
+ // -- Pure helpers ------------------------------------------------------
16
16
 
17
17
  const BOUND = Symbol('r2');
18
18
 
@@ -43,12 +43,12 @@ export function parsePush(expr) {
43
43
  return m ? { source: m[1].trim() } : null;
44
44
  }
45
45
 
46
- // ── Mount ─────────────────────────────────────────────────────────────
46
+ // -- Mount -------------------------------------------------------------
47
47
 
48
48
  export function mount(store, root = document.body) {
49
49
  const subs = []; // { node, unsub }
50
50
 
51
- // ── Binding helpers ──
51
+ // -- Binding helpers --
52
52
 
53
53
  function addBinding(path, node, updateFn) {
54
54
  updateFn(store.get(path));
@@ -65,7 +65,7 @@ export function mount(store, root = document.body) {
65
65
  }
66
66
  }
67
67
 
68
- // ── Scan for bind-* attributes ──
68
+ // -- Scan for bind-* attributes --
69
69
 
70
70
  function scanBindings(el) {
71
71
  const nodes = el instanceof Element ? [el, ...el.querySelectorAll('*')] : [];
@@ -108,7 +108,7 @@ export function mount(store, root = document.body) {
108
108
  });
109
109
  }
110
110
 
111
- // bind-data-* dataset, bind-attr-* setAttribute
111
+ // bind-data-* -> dataset, bind-attr-* -> setAttribute
112
112
  for (const attr of Array.from(node.attributes)) {
113
113
  if (attr.name.startsWith('bind-data-')) {
114
114
  const dName = attr.name.slice(10);
@@ -128,7 +128,7 @@ export function mount(store, root = document.body) {
128
128
  }
129
129
  }
130
130
 
131
- // ── Delegated action execution ──
131
+ // -- Delegated action execution --
132
132
 
133
133
  function executeSet(raw) {
134
134
  const { path, expr } = parseSetExpr(raw);
@@ -174,7 +174,7 @@ export function mount(store, root = document.body) {
174
174
  store.set(path, evalExpr(expr, store.get(path)));
175
175
  }
176
176
 
177
- // ── Step 1: Event delegation (once, never re-wired) ──
177
+ // -- Step 1: Event delegation (once, never re-wired) --
178
178
 
179
179
  root.addEventListener('click', e => {
180
180
  const t = e.target.closest('[set]');
@@ -195,7 +195,7 @@ export function mount(store, root = document.body) {
195
195
  }
196
196
  });
197
197
 
198
- // ── Step 3: Keyed collections ──
198
+ // -- Step 3: Keyed collections --
199
199
 
200
200
  function setupCollection(container) {
201
201
  const collPath = container.getAttribute('each');
@@ -203,7 +203,7 @@ export function mount(store, root = document.body) {
203
203
  if (!collPath || !tpl) return;
204
204
 
205
205
  const templateHTML = tpl.innerHTML.trim();
206
- const rendered = new Map(); // key element
206
+ const rendered = new Map(); // key -> element
207
207
  let lastKeyStr = '';
208
208
 
209
209
  function resolve(html, key) {
@@ -257,7 +257,7 @@ export function mount(store, root = document.body) {
257
257
  reconcile();
258
258
  }
259
259
 
260
- // ── Init ──
260
+ // -- Init --
261
261
 
262
262
  root.querySelectorAll('[each]').forEach(setupCollection);
263
263
  scanBindings(root);
package/self-test.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @uistate/renderer self-test
2
+ * @uistate/renderer: self-test
3
3
  *
4
4
  * Standalone test of pure functions. No dependencies beyond renderer.js.
5
5
  * Runs on `node self-test.js` or as a postinstall hook.
@@ -31,7 +31,7 @@ assert('parseSetExpr: expr', p1.expr === 'increment');
31
31
 
32
32
  const p2 = parseSetExpr('count');
33
33
  assert('parseSetExpr: path only', p2.path === 'count');
34
- assert('parseSetExpr: no expr null', p2.expr === null);
34
+ assert('parseSetExpr: no expr -> null', p2.expr === null);
35
35
 
36
36
  const p3 = parseSetExpr('user.name:Bob');
37
37
  assert('parseSetExpr: dotted path', p3.path === 'user.name');
@@ -66,8 +66,8 @@ assert('evalExpr: increment from 0', evalExpr('increment', 0) === 1);
66
66
  assert('evalExpr: increment from null', evalExpr('increment', null) === 1);
67
67
  assert('evalExpr: decrement', evalExpr('decrement', 5) === 4);
68
68
  assert('evalExpr: decrement from 0', evalExpr('decrement', 0) === -1);
69
- assert('evalExpr: toggle truefalse', evalExpr('toggle', true) === false);
70
- assert('evalExpr: toggle falsetrue', evalExpr('toggle', false) === true);
69
+ assert('evalExpr: toggle true->false', evalExpr('toggle', true) === false);
70
+ assert('evalExpr: toggle false->true', evalExpr('toggle', false) === true);
71
71
  assert('evalExpr: number 42', evalExpr('42', 0) === 42);
72
72
  assert('evalExpr: number 0', evalExpr('0', 99) === 0);
73
73
  assert('evalExpr: negative number', evalExpr('-5', 0) === -5);
@@ -75,24 +75,24 @@ assert('evalExpr: boolean true', evalExpr('true', 0) === true);
75
75
  assert('evalExpr: boolean false', evalExpr('false', 1) === false);
76
76
  assert('evalExpr: null keyword', evalExpr('null', 'x') === null);
77
77
  assert('evalExpr: plain string', evalExpr('hello', '') === 'hello');
78
- assert('evalExpr: null expr returns current', evalExpr(null, 7) === 7);
79
- assert('evalExpr: undefined expr returns current', evalExpr(undefined, 7) === 7);
78
+ assert('evalExpr: null expr -> returns current', evalExpr(null, 7) === 7);
79
+ assert('evalExpr: undefined expr -> returns current', evalExpr(undefined, 7) === 7);
80
80
 
81
81
  console.log('\n3. parsePush');
82
82
  const pp1 = parsePush('push');
83
- assert('parsePush: bare push source null', pp1 !== null && pp1.source === null);
83
+ assert('parsePush: bare push -> source null', pp1 !== null && pp1.source === null);
84
84
 
85
85
  const pp2 = parsePush('push(draft)');
86
- assert('parsePush: push(draft) source "draft"', pp2 !== null && pp2.source === 'draft');
86
+ assert('parsePush: push(draft) -> source "draft"', pp2 !== null && pp2.source === 'draft');
87
87
 
88
88
  const pp3 = parsePush('push(form.data)');
89
- assert('parsePush: push(form.data) source "form.data"', pp3 !== null && pp3.source === 'form.data');
89
+ assert('parsePush: push(form.data) -> source "form.data"', pp3 !== null && pp3.source === 'form.data');
90
90
 
91
91
  assert('parsePush: non-push returns null', parsePush('increment') === null);
92
92
  assert('parsePush: null returns null', parsePush(null) === null);
93
93
  assert('parsePush: empty string returns null', parsePush('') === null);
94
94
 
95
- // ── Results ─────────────────────────────────────────────────────────
95
+ // -- Results ---------------------------------------------------------
96
96
 
97
97
  console.log(`\n@uistate/renderer v1.0.0 — self-test`);
98
98
  console.log(`✓ ${passed} assertions passed${failed ? `, ✗ ${failed} failed` : ''}\n`);