@papack/csr 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,72 +1,45 @@
1
1
  # @papack/csr
2
2
 
3
- Async UI Dom runtime (experimental) .Designed for **predictability over comfort** — which, in practice, _creates_ comfort.
4
- No Virtual DOM. No content diffing.
5
- Instead: **real mutation**, **async components**, **deterministic structural updates**.
3
+ **Experimental synchronous UI DOM runtime.**
4
+ Designed for **simplicity, explicitness, and predictability**.
6
5
 
7
- Zero dependencies. Fully TypeScript.
6
+ > **No feature is the feature.**
8
7
 
9
- ## Features
10
-
11
- - Async components (`await` directly inside components)
12
- - Deterministic rendering without a Virtual DOM
13
- - `signal` / `effect` as minimal reactivity primitives
14
- - Explicit lifecycle (`mount`, `unmount`, `destroy`)
15
- - Keyed `For` with stable DOM identity
16
- - Structural conditional rendering (`Show`)
17
- - Mutation is allowed (by design)
18
- - Fully TypeScript
19
-
20
- ## Core Ideas
21
-
22
- - **Async components are first-class**
8
+ No Virtual DOM.
9
+ No diffing.
10
+ No scheduler.
11
+ No hidden async.
23
12
 
24
- ```ts
25
- async function User() {
26
- const data = await fetch("/api/user").then((r) => r.json());
27
- return <div>{data.name}</div>;
28
- }
29
- ```
13
+ Just **real DOM**, **real mutation**, and **deterministic structure** with zero dependencies.
30
14
 
31
- - **Signals are active sources**
15
+ ---
32
16
 
33
- - no reference equality checks
34
- - mutation is allowed
35
- - every `set()` reliably triggers effects
17
+ ## Philosophy
36
18
 
37
- - **Lifecycle is bound to real DOM nodes**
19
+ This library is **simple by design**, not “easy”.
38
20
 
39
- - `mount` / `unmount` attach to actual elements
40
- - for: reordering ≠ remounting
41
- - removal = real `destroy`
21
+ - There is **one way** to do things
22
+ - Everything is **explicit**
42
23
 
43
- ## Example
24
+ It _is_ optimized for **clarity and long-term maintenance**.
44
25
 
45
- ```ts
46
- import { jsx, render, signal } from "../core";
47
- import { For } from "../core/for";
48
-
49
- const [items, setItems] = signal([
50
- { uuid: "a", name: "A" },
51
- { uuid: "b", name: "B" },
52
- ]);
26
+ ---
53
27
 
54
- render(<App />, { parent: document.body });
28
+ ## Features
55
29
 
56
- function App() {
57
- return (
58
- <ul>
59
- <For each={items}>{(item) => <li>{item.name}</li>}</For>
60
- </ul>
61
- );
62
- }
63
- ```
30
+ - Fully **synchronous rendering**
31
+ - No Virtual DOM, no reconciliation
32
+ - Minimal reactivity via `signal` / `effect`
33
+ - Explicit lifecycle (`mount`, `unmount`)
34
+ - Structural primitives (`Show`, `For`)
64
35
 
65
36
  ---
66
37
 
67
- ## `signal`
38
+ ## Core Ideas
39
+
40
+ ### Signals are active state
68
41
 
69
- Signals are **active state containers**, not passive values.
42
+ Signals are **sources**, not values.
70
43
 
71
44
  ```ts
72
45
  const [count, setCount] = signal(0);
@@ -75,16 +48,12 @@ setCount((v) => v + 1);
75
48
  setCount(() => 42);
76
49
  ```
77
50
 
78
- ### Properties
79
-
80
- - `set()` **always triggers**
81
51
  - no equality checks
52
+ - no dependency tracking
82
53
  - mutation is allowed
83
- - async setters are allowed
84
-
85
- ## `effect(readFn, callback)`
54
+ - every write notifies every subscriber
86
55
 
87
- Subscribes to a signal and reacts to changes.
56
+ ### Effects are explicit reactions to signals
88
57
 
89
58
  ```ts
90
59
  effect(count, (value) => {
@@ -92,132 +61,117 @@ effect(count, (value) => {
92
61
  });
93
62
  ```
94
63
 
95
- ### Characteristics
64
+ Characteristics:
96
65
 
97
66
  - runs immediately with the current value
98
- - runs on **every** `set()`
99
- - no dependency tracking
100
- - async callbacks supported
67
+ - runs on **every** write
68
+ - only one singal, no dependency arrays
69
+ - async callbacks are allowed (fire-and-forget)
101
70
 
102
- ```ts
103
- effect(userId, async (id) => {
104
- const user = await fetch(`/api/user/${id}`).then((r) => r.json());
105
- });
106
- ```
71
+ > Effects are **not awaited**.
72
+ > Concurrency is explicit and intentional.
107
73
 
108
- ````md
109
- ## Context Injection
74
+ ### Lifecycle is bound to real DOM
110
75
 
111
- `render()` accepts arbitrary values on the top-level context.
112
- This context is automatically available in **every component** via `props.ctx`.
76
+ Lifecycle is structural, not conceptual.
113
77
 
114
78
  ```ts
115
- render(<App />, {
116
- parent: document.body,
117
- api,
118
- events,
119
- dummy: 42,
79
+ mount((parent) => {
80
+ // runs once, when the root DOM element exists
120
81
  });
121
- ```
122
- ````
123
82
 
124
- ```ts
125
- function Item(p: any) {
126
- console.log(p.ctx.dummy); // 42
127
- p.ctx.api.fetch();
128
- }
83
+ unmount(() => {
84
+ // guaranteed cleanup before removal
85
+ });
129
86
  ```
130
87
 
131
- - no providers
132
- - no hooks
133
- - no imports
134
- - no reactivity
135
-
136
- Context is for **stable infrastructure** (stores, APIs, event buses),
137
- not for frequently changing UI state.
88
+ - lifecycle is attached to **actual DOM nodes**
89
+ - children unmount before parents
90
+ - removal means real `destroy`
138
91
 
139
- The context is immutable for the lifetime of the render tree and
140
- does not trigger re-renders.
92
+ ---
141
93
 
142
- ## Lifecycle Primitives
94
+ ## Example
143
95
 
144
- Lifecycle is **explicit** and **structural**.
96
+ ```ts
97
+ import { render, signal } from "@papack/csr";
98
+ import { For } from "@papack/csr/for";
145
99
 
146
- ### `mount(fn)`
100
+ const [items, setItems] = signal([
101
+ { uuid: "a", name: "A" },
102
+ { uuid: "b", name: "B" },
103
+ ]);
147
104
 
148
- Registers a callback that runs **once**, when the component’s root DOM element
149
- is attached.
105
+ render(<App />, { parent: document.body });
150
106
 
151
- ```ts
152
- mount((parent) => {
153
- // parent === root Element
154
- });
107
+ function App() {
108
+ return (
109
+ <ul>
110
+ <For each={items}>{(item) => <li>{item.name}</li>}</For>
111
+ </ul>
112
+ );
113
+ }
155
114
  ```
156
115
 
157
- - runs exactly once per component instance
158
- - runs after the DOM node exists
159
- - used for subscriptions, timers, imperative DOM work
116
+ ## Structural Rendering
160
117
 
161
- ### `unmount(fn)`
118
+ ### `Show`
162
119
 
163
- Registers cleanup logic tied to the component’s root element.
120
+ Controls **existence**, not visibility.
164
121
 
165
122
  ```ts
166
- unmount(() => {
167
- // cleanup
168
- });
123
+ <Show when={visible}>
124
+ <User />
125
+ </Show>
169
126
  ```
170
127
 
171
- - runs exactly once
172
- - runs before DOM removal
173
- - children unmount before parents
174
- - guaranteed execution
128
+ - when `false`, the subtree is destroyed
129
+ - when `true`, it is rendered fresh
130
+ - lifecycle runs correctly on both transitions
175
131
 
176
- ## `For` (intentionally restricted)
132
+ ### `For` (intentionally restricted)
177
133
 
178
- `For` is **not** a general iterator.
179
- It is a **keyed structural renderer**.
134
+ `For` is a **keyed structural renderer**, not a generic iterator.
180
135
 
181
- ### Rules
136
+ Rules:
182
137
 
183
- - `each` **must** bet an array
184
- - each item **must** be an object
185
- - each object **must** have a stable key field (e.g. `uuid`)
186
- - no fallbacks, no warnings
138
+ - `each` must be an array
139
+ - each item must be an object
140
+ - each item must have a stable key (`uuid`)
141
+ - no fallbacks, no heuristics
187
142
 
188
143
  ```ts
189
144
  type Item = {
190
- uuid: string; // required
145
+ uuid: string;
191
146
  [key: string]: any;
192
147
  };
193
148
  ```
194
149
 
195
- ### What `For` does
150
+ What `For` does:
196
151
 
197
152
  - detects:
198
153
 
154
+ - additions
155
+ - removals
199
156
  - order changes
200
- - added items
201
- - removed items
202
157
 
203
158
  - performs:
204
159
 
205
160
  - DOM moves (`insertBefore`)
206
- - rendering **only** for new keys
161
+ - rendering only for new keys
207
162
  - `destroy()` for removed keys
208
163
 
209
- ### What `For` does **not** do
164
+ What `For` does **not** do:
210
165
 
211
166
  - no content diffing
212
167
  - no re-rendering existing items
213
168
  - no prop patching
214
- - no heuristic matching
215
169
 
216
170
  > If an item’s content changes, the item itself must be reactive.
217
171
 
218
172
  ---
219
173
 
220
- ## Mutation: allowed
174
+ ## Mutation is allowed
221
175
 
222
176
  ```ts
223
177
  setItems((prev) => {
@@ -230,16 +184,33 @@ This is **correct**.
230
184
 
231
185
  Why:
232
186
 
233
- - `signal` is active
234
- - effects are not reference-based
235
- - `For` evaluates only keys and order
187
+ - signals are active
188
+ - updates are not reference-based
189
+ - `For` only cares about keys and order
236
190
 
237
- ## `Show` (structural conditional rendering)
191
+ ## Routing
238
192
 
239
- `Show` controls **existence**, not visibility.
193
+ Routing is **just application state**.
240
194
 
241
195
  ```ts
242
- <Show when={visible}>
243
- <User />
196
+ const [route, setRoute] = signal("home");
197
+ ```
198
+
199
+ Structure is derived explicitly:
200
+
201
+ ```ts
202
+ effect(route, (r) => {
203
+ setIsHome(() => r === "home");
204
+ setIsSettings(() => r === "settings");
205
+ });
206
+ ```
207
+
208
+ ```tsx
209
+ <Show when={isHome}>
210
+ <Home />
211
+ </Show>
212
+
213
+ <Show when={isSettings}>
214
+ <Settings />
244
215
  </Show>
245
216
  ```