@flexsurfer/reflex 0.1.24 → 0.1.25

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 (3) hide show
  1. package/README.md +29 -1
  2. package/llms.txt +186 -0
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -32,11 +32,39 @@ After many years of building applications with re-frame in the ClojureScript wor
32
32
  - [Step-by-Step Tutorial](https://reflex.js.org/docs/quick-start.html)
33
33
  - [Best Practices](https://reflex.js.org/docs/api-reference.html)
34
34
  - [API Reference](https://reflex.js.org/docs/best-practices.html)
35
+ - [AI Context (llms.txt)](./llms.txt) - Compact guide for AI-assisted Reflex project scaffolding and state architecture
35
36
  - [re-frame Documentation](https://day8.github.io/re-frame/re-frame/) - The original and comprehensive guide to understanding the philosophy and patterns
36
37
 
37
38
  - Examples
38
39
  - [TodoMVC](https://github.com/flexsurfer/reflex/tree/main/examples/todomvc) - Classic todo app implementation showcasing core reflex patterns
39
- - [Einbürgerungstest](https://github.com/flexsurfer/einburgerungstest/) - German citizenship test app built with reflex ([Live Demo](https://www.ebtest.org/))
40
+ - [Einbürgerungstest](https://github.com/flexsurfer/einburgerungstest/) - Cross-platform web/mobile app built with reflex ([Live Demo](https://www.ebtest.org/))
41
+ - [StarRupture Planner](https://github.com/flexsurfer/starrupture-planner) - Production planning tool built with reflex ([Live Demo](https://www.starrupture-planner.com/))
42
+
43
+ ## 🤖 Using with AI Assistants
44
+
45
+ Reflex ships an [`llms.txt`](./llms.txt) file — a compact, AI-readable guide covering state architecture, event/effect/subscription patterns, and code generation rules. Point your AI tool at it so it generates idiomatic Reflex code from the start.
46
+
47
+ **Claude Code** — add to `CLAUDE.md` in your project root:
48
+ ```bash
49
+ curl -o CLAUDE.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
50
+ ```
51
+
52
+ **Codex (OpenAI)** — add to `AGENTS.md` in your project root:
53
+ ```bash
54
+ curl -o AGENTS.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
55
+ ```
56
+ Codex reads project instructions from `AGENTS.md`, so this gives it Reflex-specific architecture and code generation rules for your repo.
57
+
58
+ **Cursor** — create `.cursor/rules/reflex.mdc` and paste the contents, or reference via project rules.
59
+
60
+ **GitHub Copilot** — add to `.github/copilot-instructions.md`:
61
+ ```bash
62
+ curl -o .github/copilot-instructions.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
63
+ ```
64
+
65
+ **ChatGPT / Claude.ai Projects** — upload `llms.txt` from your `node_modules/@flexsurfer/reflex/` as a project file, or paste the raw URL into the conversation.
66
+
67
+ The file is also included in the npm package, so after installing Reflex you can find it at `node_modules/@flexsurfer/reflex/llms.txt`.
40
68
 
41
69
  ## 🤝 Contributing
42
70
 
package/llms.txt ADDED
@@ -0,0 +1,186 @@
1
+ ## 0) Reflex Quick Info
2
+
3
+ - Reflex is a React state-management library inspired by ClojureScript re-frame (event-driven updates, subscriptions for derived data, effects/coeffects for side effects).
4
+ - Install:
5
+ - Runtime: `npm i @flexsurfer/reflex`
6
+ - Devtools (dev only): `npm i -D @flexsurfer/reflex-devtools`
7
+ - Docs:
8
+ - Main docs: [reflex.js.org/docs](https://reflex.js.org/docs)
9
+ - Best practices: [reflex.js.org/docs/best-practices.html](https://reflex.js.org/docs/best-practices.html)
10
+ - Packages: [@flexsurfer/reflex](https://www.npmjs.com/package/@flexsurfer/reflex), [@flexsurfer/reflex-devtools](https://www.npmjs.com/package/@flexsurfer/reflex-devtools)
11
+
12
+ ## 1) State Architecture
13
+
14
+ Use this baseline structure:
15
+
16
+ ```text
17
+ src/state/
18
+ db.ts
19
+ event-ids.ts
20
+ events.ts
21
+ effect-ids.ts
22
+ effects.ts
23
+ sub-ids.ts
24
+ subs.ts
25
+ ```
26
+
27
+ Rules:
28
+ - Keep IDs centralized in `event-ids.ts`, `effect-ids.ts`, `sub-ids.ts`.
29
+ - Keep init in `db.ts` (`initAppDb(...)`).
30
+ - Register events/effects/subscriptions via side-effect imports in app bootstrap (`main.tsx` style).
31
+ - If `events.ts` or `subs.ts` grows too large, split by feature, keep shared/global state separate.
32
+
33
+ ## 2) State Shape
34
+
35
+ - Grow horizontally (new top-level feature keys), avoid deep nesting.
36
+ - Normalize entity-like data (`byId` maps + id arrays) for fast lookup and simpler updates.
37
+ - Keep UI state explicit and separate from domain entities.
38
+ - If using `Map`/`Set` in DB, call `enableMapSet()` before `initAppDb`.
39
+
40
+ ## 3) Events `regEvent`
41
+
42
+ - Events must be synchronous and focused on state transitions.
43
+ - Events should read all required data from `draftDb` (or via coeffects) — never rely on callers passing subscription-derived state; dispatch calls from views should only carry user intent (IDs, input values, flags).
44
+ - Validate inputs and guard clauses first; return early on invalid state.
45
+ - Mutate only required fields on `draftDb`.
46
+ - Avoid unnecessary object/array recreation (`{...obj}`, `[...arr]`) when no actual change is needed.
47
+ - Never perform async/API/localStorage work directly in events.
48
+ - Return effect tuples for side effects.
49
+ - When sending mutated draft data to effects, always use `current(...)`.
50
+ - Prefer deterministic coeffects (time/id/random/env) instead of direct globals for testability.
51
+
52
+ Event naming:
53
+ - Keep exported constant keys in `UPPER_SNAKE_CASE`.
54
+ - Use namespaced string values: `feature/action` (example: `bases/create`).
55
+ - Keep key and value aligned:
56
+ - `BASES_CREATE` -> `bases/create`
57
+ - `PRODUCTION_PLAN_ADD_BUILDINGS_TO_BASE` -> `production_plan/add_buildings_to_base`
58
+
59
+ ## 4) Effects and Coeffects
60
+
61
+ - Put all I/O here: localStorage, HTTP, timers, analytics, navigation.
62
+ - Effects should be small, defensive, and fail-soft (log, do not crash app state flow).
63
+
64
+ ## 5) Subscriptions `regSub`
65
+
66
+ - Define root subscriptions first `regSub(id, "pathKey")`.
67
+ - Build derived subscriptions from other subscriptions only.
68
+ - Use parameterized subscriptions for by-id and section-specific queries.
69
+ - Keep subscriptions deterministic and lightweight.
70
+ - Subscriptions must return data shaped and ready for direct view consumption — all filtering, sorting, formatting, and joining should happen in the subscription layer, not in React components.
71
+ - Move heavy computations to events (precompute once, read many); avoid repeated expensive derivations in hot subscriptions.
72
+
73
+ ## 6) React Component Contract
74
+
75
+ - Components should only:
76
+ - subscribe to minimal required data
77
+ - dispatch events on user intent (pass only user-provided values — e.g. input text, selected id — never forward subscription data back through dispatch; the event handler should read everything it needs from the DB itself)
78
+ - render UI
79
+ - Never transform, filter, sort, or reshape subscription data inside a component — if the view needs a different shape, create a dedicated subscription that returns it ready to render.
80
+ - Use direct React hooks only for local/ephemeral component concerns:
81
+ - temporary form/input draft state before dispatch
82
+ - UI-only toggles scoped to one component (hover/open/focus)
83
+ - refs, DOM measurement, animation lifecycle
84
+ - Do not mirror Reflex global state in `useState`/`useReducer`.
85
+ - If state is shared, persisted, or business-relevant, keep it in Reflex DB via events/subscriptions.
86
+ - Keep `useEffect` thin in components; business side effects belong in Reflex effects/coeffects.
87
+ - Do not place business rules/validation pipelines in component handlers.
88
+ - Avoid over-subscription (row/item components should not subscribe to full collections).
89
+
90
+ ## 7) Test Minimum
91
+
92
+ For every new feature:
93
+ - Event tests: mutation correctness + emitted effect tuples.
94
+ - Subscription tests: derived outputs from fixed state fixtures.
95
+
96
+ ## 8) AI Generation Checklist
97
+
98
+ Before finalizing generated code:
99
+ - IDs added/updated in all relevant `*-ids.ts` files.
100
+ - Event namespaced and descriptive.
101
+ - Side effects isolated to effects/coeffects.
102
+ - `current(...)` used when passing draft-derived data to effects.
103
+ - No unnecessary object/array recreation in events.
104
+ - Expensive work not placed in frequently re-run subscriptions.
105
+ - Subscriptions return view-ready data; components do not reshape subscription output.
106
+ - Dispatch calls from components pass only user intent, not subscription data; events read from DB.
107
+ - Tests added for new event/subscription behavior.
108
+
109
+ ## 9) Starter Skeleton (copy pattern)
110
+
111
+ ```ts
112
+ import {
113
+ initAppDb,
114
+ regSub,
115
+ regEvent,
116
+ regEffect,
117
+ regCoeffect,
118
+ current,
119
+ dispatch,
120
+ useSubscription,
121
+ } from '@flexsurfer/reflex';
122
+
123
+ // event-ids.ts
124
+ export const EVENT_IDS = {
125
+ APP_INIT: 'app/init',
126
+ TODOS_ADD: 'todos/add',
127
+ } as const;
128
+
129
+ // effect-ids.ts
130
+ export const EFFECT_IDS = {
131
+ GET_TODOS: 'storage/get_todos',
132
+ SET_TODOS: 'storage/set_todos',
133
+ } as const;
134
+
135
+ // sub-ids.ts
136
+ export const SUB_IDS = {
137
+ TODOS_LIST: 'todos/list', // root sub
138
+ TODOS_OPEN_COUNT: 'todos/open_count', // computed sub
139
+ } as const;
140
+
141
+ // db.ts
142
+ type Todo = { id: string; text: string; done: boolean };
143
+ initAppDb({ todos: [] as Todo[] });
144
+
145
+ // effects.ts
146
+ regEffect(EFFECT_IDS.SET_TODOS, (todos: Todo[]) => {
147
+ localStorage.setItem('todos', JSON.stringify(todos));
148
+ });
149
+
150
+ regCoeffect(EFFECT_IDS.GET_TODOS, (coeffects) => {
151
+ const raw = localStorage.getItem('todos');
152
+ coeffects.localStoreTodos = raw ? (JSON.parse(raw) as Todo[]) : [];
153
+ return coeffects;
154
+ });
155
+
156
+ // events.ts
157
+ regEvent(
158
+ EVENT_IDS.APP_INIT,
159
+ ({ draftDb, localStoreTodos }) => {
160
+ draftDb.todos = Array.isArray(localStoreTodos) ? localStoreTodos : [];
161
+ },
162
+ [[EFFECT_IDS.GET_TODOS]]
163
+ );
164
+
165
+ regEvent(EVENT_IDS.TODOS_ADD, ({ draftDb }, text: string) => {
166
+ const clean = text.trim();
167
+ if (!clean) return;
168
+ draftDb.todos.push({ id: `todo_${Date.now()}`, text: clean, done: false });
169
+ return [[EFFECT_IDS.SET_TODOS, current(draftDb.todos)]];
170
+ });
171
+
172
+ // subs.ts
173
+ regSub(SUB_IDS.TODOS_LIST, 'todos'); // root sub
174
+
175
+ regSub(
176
+ SUB_IDS.TODOS_OPEN_COUNT, // computed sub from root sub
177
+ (todos: Todo[]) => todos.filter((t) => !t.done).length,
178
+ () => [[SUB_IDS.TODOS_LIST]]
179
+ );
180
+
181
+ // Usage example (React):
182
+ // const todos = useSubscription([SUB_IDS.TODOS_LIST]);
183
+ // const openCount = useSubscription([SUB_IDS.TODOS_OPEN_COUNT]);
184
+ // dispatch([EVENT_IDS.APP_INIT]);
185
+ // dispatch([EVENT_IDS.TODOS_ADD, 'Buy milk']);
186
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexsurfer/reflex",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,8 @@
23
23
  "files": [
24
24
  "dist",
25
25
  "README.md",
26
- "LICENSE"
26
+ "LICENSE",
27
+ "llms.txt"
27
28
  ],
28
29
  "scripts": {
29
30
  "build": "tsup",