@flexsurfer/reflex 0.1.12 โ 0.1.14
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 +9 -251
- package/dist/index.cjs +57 -38
- package/dist/index.d.cts +29 -16
- package/dist/index.d.ts +29 -16
- package/dist/index.mjs +48 -33
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="reflex_logo.jpg" alt="Reflex Logo" width="200" />
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
5
|
**re-frame for the JavaScript world**
|
|
6
6
|
|
|
7
|
-
A reactive, functional state management library that brings the elegance and power of ClojureScript's re-frame to JavaScript and React/ReactNative applications.
|
|
7
|
+
A reactive, functional state management library that brings the elegance and power of ClojureScript's re-frame to JavaScript/TypeScript and React/ReactNative applications.
|
|
8
8
|
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
[](https://www.npmjs.com/package/@flexsurfer/reflex)
|
|
@@ -20,265 +20,23 @@ After many years of building applications with re-frame in the ClojureScript wor
|
|
|
20
20
|
๐งฉ **Composable Architecture** - Build complex apps from simple, reusable pieces
|
|
21
21
|
๐ **Reactive Subscriptions** - UI automatically updates when state changes
|
|
22
22
|
๐ **Multi-Platform Support** - With effects separation, it's super easy to support multiple platforms with the same codebase, including web, mobile, and desktop
|
|
23
|
-
๐ค **AI Friendly** - Reviewing AI-generated changes is easier because all logic is expressed through pure, isolated functions, making each change understandable, verifiable, and deterministic.
|
|
24
|
-
๐ ๏ธ **Integrated DevTools** -
|
|
23
|
+
๐ค **AI Friendly** - Reviewing AI-generated changes is easier because all logic is expressed through pure, isolated functions, making each change understandable, verifiable, and deterministic.
|
|
24
|
+
๐ ๏ธ **Integrated DevTools** - [`@flexsurfer/reflex-devtools`](https://github.com/flexsurfer/reflex-devtools) provides deep visibility into your appโs state, events, and subscriptions in real time, forming a powerful combo with Reflex for effective development and debugging.
|
|
25
25
|
โก **Interceptor Pattern** - Powerful middleware system for cross-cutting concerns
|
|
26
26
|
๐ก๏ธ **Type Safety** - Full TypeScript support with excellent IDE experience
|
|
27
27
|
๐งช **Testability** - Pure functions make testing straightforward and reliable
|
|
28
28
|
|
|
29
|
-
## ๐ Quick Start
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npm install @flexsurfer/reflex
|
|
33
|
-
npm install --save-dev @flexsurfer/reflex-devtools
|
|
34
|
-
|
|
35
|
-
npx reflex-devtools
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Basic Example
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
import {
|
|
42
|
-
initAppDb,
|
|
43
|
-
regEvent,
|
|
44
|
-
regSub,
|
|
45
|
-
dispatch,
|
|
46
|
-
useSubscription,
|
|
47
|
-
enableTracing
|
|
48
|
-
} from '@flexsurfer/reflex';
|
|
49
|
-
import { enableDevtools } from '@flexsurfer/reflex-devtools'
|
|
50
|
-
|
|
51
|
-
enableTracing()
|
|
52
|
-
enableDevtools();
|
|
53
|
-
|
|
54
|
-
// Initialize your app database
|
|
55
|
-
initAppDb({ counter: 0 });
|
|
56
|
-
|
|
57
|
-
// Register events (state transitions)
|
|
58
|
-
regEvent('increment', ({ draftDb }) => {
|
|
59
|
-
draftDb.counter += 1;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
regEvent('decrement', ({ draftDb }) => {
|
|
63
|
-
draftDb.counter -= 1;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Register subscriptions (reactive queries)
|
|
67
|
-
regSub('counter');
|
|
68
|
-
|
|
69
|
-
// React component
|
|
70
|
-
const Counter = () => {
|
|
71
|
-
const counter = useSubscription<number>(['counter']);
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<div>
|
|
75
|
-
<h1>Count: {counter}</h1>
|
|
76
|
-
<button onClick={() => dispatch(['increment'])}>+</button>
|
|
77
|
-
<button onClick={() => dispatch(['decrement'])}>-</button>
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## ๐๏ธ Core Concepts
|
|
84
|
-
|
|
85
|
-
### Events & Effects
|
|
86
|
-
|
|
87
|
-
Events define state transitions and may declare side effects:
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
// Simple state update
|
|
91
|
-
regEvent('set-name', ({ draftDb }, name) => {
|
|
92
|
-
draftDb.user.name = name;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Dispatch with parameters
|
|
96
|
-
dispatch(['set-name', 'John Doe']);
|
|
97
|
-
|
|
98
|
-
// Event with side effects
|
|
99
|
-
regEvent('save-user', ({ draftDb }, user) => {
|
|
100
|
-
draftDb.saving = true;
|
|
101
|
-
return [
|
|
102
|
-
['http', {
|
|
103
|
-
method: 'POST',
|
|
104
|
-
url: '/api/users',
|
|
105
|
-
body: user,
|
|
106
|
-
onSuccess: ['save-user-success'],
|
|
107
|
-
onFailure: ['save-user-error']
|
|
108
|
-
}]
|
|
109
|
-
]
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Dispatch with parameters
|
|
113
|
-
dispatch(['save-user', { id: 1, name: 'John', email: 'john@example.com' }]);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Subscriptions
|
|
117
|
-
|
|
118
|
-
Create reactive queries that automatically update your UI:
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
regSub('user');
|
|
122
|
-
regSub('display-prefix');
|
|
123
|
-
regSub('user-name', (user) => user.name, () => [['user']]);
|
|
124
|
-
|
|
125
|
-
// Computed subscription with dependencies
|
|
126
|
-
regSub('user-display-name',
|
|
127
|
-
(name, prefix) => `${prefix}: ${name}`,
|
|
128
|
-
() => [['user-name'], ['display-prefix']]
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Parameterized subscription
|
|
132
|
-
regSub(
|
|
133
|
-
'todo-by-id',
|
|
134
|
-
(todos, id) => todos.find(todo => todo.id === id),
|
|
135
|
-
() => [['todos']]
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
regSub(
|
|
139
|
-
'todo-text-by-id',
|
|
140
|
-
(todo, _id) => todo.text,
|
|
141
|
-
(id) => [['todo-by-id' id]]
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
// Use in React components
|
|
145
|
-
function UserProfile() {
|
|
146
|
-
const name = useSubscription<string>(['user-display-name']);
|
|
147
|
-
const todo = useSubscription(['todo-by-id', 123])
|
|
148
|
-
const todoText = useSubscription(['todo-text-by-id', 123]);
|
|
149
|
-
|
|
150
|
-
return <div>{name}</div>;
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Effects & Co-effects
|
|
155
|
-
|
|
156
|
-
Handle side effects in a controlled, testable way:
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
import {
|
|
160
|
-
regEffect,
|
|
161
|
-
regCoeffect
|
|
162
|
-
} from '@flexsurfer/reflex';
|
|
163
|
-
|
|
164
|
-
// Register custom effects
|
|
165
|
-
regEffect('local-storage', (payload) => {
|
|
166
|
-
localStorage.setItem(payload.key, JSON.stringify(payload.value));
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Use in events
|
|
170
|
-
regEvent('save-to-storage', (_coeffects, data) => {
|
|
171
|
-
return [['local-storage', { key: 'app-data', value: data }]]
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// Dispatch with data parameter
|
|
175
|
-
dispatch(['save-to-storage', { user: 'John', preferences: { theme: 'dark' } }]);
|
|
176
|
-
|
|
177
|
-
// Register co-effects
|
|
178
|
-
regCoeffect('timestamp', (coeffects) => {
|
|
179
|
-
coeffects.timestamp = Date.now();
|
|
180
|
-
return coeffects;
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
regCoeffect('random', (coeffects) => {
|
|
184
|
-
coeffects.random = Math.random();
|
|
185
|
-
return coeffects;
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Use co-effect in events
|
|
189
|
-
regEvent('log-action',
|
|
190
|
-
({ draftDb, timestamp, random }, action) => {
|
|
191
|
-
draftDb.actionLog.push({
|
|
192
|
-
action,
|
|
193
|
-
timestamp: timestamp,
|
|
194
|
-
id: random.toString(36)
|
|
195
|
-
});
|
|
196
|
-
},
|
|
197
|
-
[['timestamp'], ['random']]
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// Dispatch with action parameter
|
|
201
|
-
dispatch(['log-action', 'some-action']);
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Interceptors
|
|
205
|
-
|
|
206
|
-
Compose functionality with interceptors:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
const loggingInterceptor = {
|
|
210
|
-
id: 'logging',
|
|
211
|
-
before: (context) => {
|
|
212
|
-
console.log('Event:', context.coeffects.event);
|
|
213
|
-
return context;
|
|
214
|
-
},
|
|
215
|
-
after: (context) => {
|
|
216
|
-
console.log('Updated DB:', context.coeffects.newDb);
|
|
217
|
-
return context;
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
regEvent('my-event', handler, [loggingInterceptor]);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## ๐ฏ Why Re-frame Pattern?
|
|
225
|
-
|
|
226
|
-
The re-frame pattern has proven itself in production applications over many years:
|
|
227
|
-
|
|
228
|
-
- **Separation of Concerns**: Clear boundaries between events, effects, and subscriptions
|
|
229
|
-
- **Time Travel Debugging**: Every state change is an event that can be replayed
|
|
230
|
-
- **Testability**: Pure functions make unit testing straightforward
|
|
231
|
-
- **Composability**: Build complex features from simple, reusable parts
|
|
232
|
-
- **Maintainability**: Code becomes self-documenting and easy to reason about
|
|
233
|
-
|
|
234
|
-
## ๐ Migration from Other Libraries
|
|
235
|
-
|
|
236
|
-
### From Redux
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
// Redux style
|
|
240
|
-
const counterSlice = createSlice({
|
|
241
|
-
name: 'count',
|
|
242
|
-
initialState: { value: 0 },
|
|
243
|
-
reducers: {
|
|
244
|
-
increment: (state) => { state.value += 1; }
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// Reflex style
|
|
249
|
-
initAppDb({ count: 0 });
|
|
250
|
-
regEvent('increment', ({ draftDb }) => {
|
|
251
|
-
draftDb.count += 1;
|
|
252
|
-
});
|
|
253
|
-
regSub('count');
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### From Zustand
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
// Zustand style
|
|
260
|
-
const useStore = create((set) => ({
|
|
261
|
-
count: 0,
|
|
262
|
-
increment: () => set((state) => ({ count: state.count + 1 }))
|
|
263
|
-
}));
|
|
264
|
-
|
|
265
|
-
// Reflex style
|
|
266
|
-
initAppDb({ count: 0 });
|
|
267
|
-
regEvent('increment', ({ draftDb }) => {
|
|
268
|
-
draftDb.count += 1;
|
|
269
|
-
});
|
|
270
|
-
regSub('count');
|
|
271
|
-
```
|
|
272
|
-
|
|
273
29
|
## ๐ Learn More
|
|
274
30
|
|
|
31
|
+
- [Documentation](https://reflex.js.org/docs/)
|
|
32
|
+
- [Step-by-Step Tutorial](https://reflex.js.org/docs/quick-start.html)
|
|
33
|
+
- [Best Practices](https://reflex.js.org/docs/api-reference.html)
|
|
34
|
+
- [API Reference](https://reflex.js.org/docs/best-practices.html)
|
|
275
35
|
- [re-frame Documentation](https://day8.github.io/re-frame/re-frame/) - The original and comprehensive guide to understanding the philosophy and patterns
|
|
276
|
-
|
|
277
|
-
- API Reference - TBD
|
|
36
|
+
|
|
278
37
|
- Examples
|
|
279
38
|
- [TodoMVC](https://github.com/flexsurfer/reflex/tree/main/examples/todomvc) - Classic todo app implementation showcasing core reflex patterns
|
|
280
39
|
- [Einbรผrgerungstest](https://github.com/flexsurfer/einburgerungstest/) - German citizenship test app built with reflex ([Live Demo](https://www.ebtest.org/))
|
|
281
|
-
- Best Practices - TBD
|
|
282
40
|
|
|
283
41
|
## ๐ค Contributing
|
|
284
42
|
|
package/dist/index.cjs
CHANGED
|
@@ -30,12 +30,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
DISPATCH: () => DISPATCH,
|
|
34
|
+
DISPATCH_LATER: () => DISPATCH_LATER,
|
|
33
35
|
HotReloadWrapper: () => HotReloadWrapper,
|
|
36
|
+
NOW: () => NOW,
|
|
37
|
+
RANDOM: () => RANDOM,
|
|
34
38
|
clearGlobalInterceptors: () => clearGlobalInterceptors,
|
|
35
39
|
clearHandlers: () => clearHandlers,
|
|
36
40
|
clearHotReloadCallbacks: () => clearHotReloadCallbacks,
|
|
37
41
|
clearReactions: () => clearReactions,
|
|
38
42
|
clearSubs: () => clearSubs,
|
|
43
|
+
current: () => current,
|
|
39
44
|
debounceAndDispatch: () => debounceAndDispatch,
|
|
40
45
|
defaultErrorHandler: () => defaultErrorHandler,
|
|
41
46
|
disableTracing: () => disableTracing,
|
|
@@ -47,7 +52,7 @@ __export(src_exports, {
|
|
|
47
52
|
getHandler: () => getHandler,
|
|
48
53
|
getSubscriptionValue: () => getSubscriptionValue,
|
|
49
54
|
initAppDb: () => initAppDb,
|
|
50
|
-
|
|
55
|
+
original: () => original,
|
|
51
56
|
regCoeffect: () => regCoeffect,
|
|
52
57
|
regEffect: () => regEffect,
|
|
53
58
|
regEvent: () => regEvent,
|
|
@@ -56,7 +61,6 @@ __export(src_exports, {
|
|
|
56
61
|
regSub: () => regSub,
|
|
57
62
|
registerHotReloadCallback: () => registerHotReloadCallback,
|
|
58
63
|
registerTraceCb: () => registerTraceCb,
|
|
59
|
-
setDebugEnabled: () => setDebugEnabled,
|
|
60
64
|
setupSubsHotReload: () => setupSubsHotReload,
|
|
61
65
|
throttleAndDispatch: () => throttleAndDispatch,
|
|
62
66
|
triggerHotReload: () => triggerHotReload,
|
|
@@ -226,6 +230,15 @@ function updateAppDbWithPatches(newDb, patches) {
|
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
|
|
233
|
+
// src/immer-utils.ts
|
|
234
|
+
var import_immer = require("immer");
|
|
235
|
+
function original(value) {
|
|
236
|
+
return (0, import_immer.isDraft)(value) ? (0, import_immer.original)(value) : value;
|
|
237
|
+
}
|
|
238
|
+
function current(value) {
|
|
239
|
+
return (0, import_immer.isDraft)(value) ? (0, import_immer.current)(value) : value;
|
|
240
|
+
}
|
|
241
|
+
|
|
229
242
|
// src/interceptor.ts
|
|
230
243
|
function isInterceptor(m) {
|
|
231
244
|
if (typeof m !== "object" || m === null)
|
|
@@ -314,8 +327,8 @@ function execute(eventV, interceptors) {
|
|
|
314
327
|
try {
|
|
315
328
|
return executeInterceptors(ctx);
|
|
316
329
|
} catch (e) {
|
|
317
|
-
const
|
|
318
|
-
errorHandler(e.cause || e,
|
|
330
|
+
const reflexError = mergeExData(e, { eventV });
|
|
331
|
+
errorHandler(e.cause || e, reflexError);
|
|
319
332
|
return ctx;
|
|
320
333
|
}
|
|
321
334
|
}
|
|
@@ -343,11 +356,13 @@ function getInjectCofxInterceptor(id, value) {
|
|
|
343
356
|
}
|
|
344
357
|
};
|
|
345
358
|
}
|
|
346
|
-
|
|
359
|
+
var NOW = "now";
|
|
360
|
+
var RANDOM = "random";
|
|
361
|
+
regCoeffect(NOW, (coeffects) => ({
|
|
347
362
|
...coeffects,
|
|
348
363
|
now: Date.now()
|
|
349
364
|
}));
|
|
350
|
-
regCoeffect(
|
|
365
|
+
regCoeffect(RANDOM, (coeffects) => ({
|
|
351
366
|
...coeffects,
|
|
352
367
|
random: Math.random()
|
|
353
368
|
}));
|
|
@@ -532,6 +547,8 @@ var doFxInterceptor = {
|
|
|
532
547
|
return context;
|
|
533
548
|
}
|
|
534
549
|
};
|
|
550
|
+
var DISPATCH_LATER = "dispatch-later";
|
|
551
|
+
var DISPATCH = "dispatch";
|
|
535
552
|
function dispatchLater(effect) {
|
|
536
553
|
const { ms, dispatch: eventToDispatch } = effect;
|
|
537
554
|
if (!Array.isArray(eventToDispatch) || typeof ms !== "number") {
|
|
@@ -543,10 +560,10 @@ function dispatchLater(effect) {
|
|
|
543
560
|
}
|
|
544
561
|
setTimeout(() => dispatch(eventToDispatch), Math.max(0, ms));
|
|
545
562
|
}
|
|
546
|
-
regEffect(
|
|
563
|
+
regEffect(DISPATCH_LATER, (value) => {
|
|
547
564
|
dispatchLater(value);
|
|
548
565
|
});
|
|
549
|
-
regEffect(
|
|
566
|
+
regEffect(DISPATCH, (value) => {
|
|
550
567
|
if (!Array.isArray(value)) {
|
|
551
568
|
consoleLog("error", "[reflex] ignoring bad dispatch value. Expected a vector, but got:", value);
|
|
552
569
|
return;
|
|
@@ -555,20 +572,15 @@ regEffect("dispatch", (value) => {
|
|
|
555
572
|
});
|
|
556
573
|
|
|
557
574
|
// src/events.ts
|
|
558
|
-
var
|
|
575
|
+
var import_immer2 = require("immer");
|
|
559
576
|
|
|
560
577
|
// src/settings.ts
|
|
561
578
|
var store = {
|
|
562
|
-
globalInterceptors: []
|
|
563
|
-
debugEnabled: true
|
|
564
|
-
// Default to true, can be configured via setDebugEnabled
|
|
579
|
+
globalInterceptors: []
|
|
565
580
|
};
|
|
566
581
|
function replaceGlobalInterceptor(globalInterceptors, interceptor) {
|
|
567
582
|
return globalInterceptors.reduce((ret, existingInterceptor) => {
|
|
568
583
|
if (interceptor.id === existingInterceptor.id) {
|
|
569
|
-
if (store.debugEnabled) {
|
|
570
|
-
consoleLog("warn", "[reflex] replacing duplicate global interceptor id:", interceptor.id);
|
|
571
|
-
}
|
|
572
584
|
return [...ret, interceptor];
|
|
573
585
|
} else {
|
|
574
586
|
return [...ret, existingInterceptor];
|
|
@@ -594,12 +606,6 @@ function clearGlobalInterceptors(id) {
|
|
|
594
606
|
store.globalInterceptors = store.globalInterceptors.filter((interceptor) => interceptor.id !== id);
|
|
595
607
|
}
|
|
596
608
|
}
|
|
597
|
-
function setDebugEnabled(enabled) {
|
|
598
|
-
store.debugEnabled = enabled;
|
|
599
|
-
}
|
|
600
|
-
function isDebugEnabled() {
|
|
601
|
-
return store.debugEnabled;
|
|
602
|
-
}
|
|
603
609
|
|
|
604
610
|
// src/trace.ts
|
|
605
611
|
var nextId = 1;
|
|
@@ -700,6 +706,13 @@ function enableTracePrint() {
|
|
|
700
706
|
});
|
|
701
707
|
}
|
|
702
708
|
|
|
709
|
+
// src/env.ts
|
|
710
|
+
var IS_DEV = (
|
|
711
|
+
// Node.js check
|
|
712
|
+
typeof process !== "undefined" && process.env?.NODE_ENV === "development" || // React Native / bundler check
|
|
713
|
+
typeof __DEV__ !== "undefined" && __DEV__
|
|
714
|
+
);
|
|
715
|
+
|
|
703
716
|
// src/events.ts
|
|
704
717
|
var KIND3 = "event";
|
|
705
718
|
function regEvent(id, handler, cofxOrInterceptors, interceptors) {
|
|
@@ -748,22 +761,28 @@ function registerInterceptors(id, cofxOrInterceptors, interceptors) {
|
|
|
748
761
|
setInterceptors(id, allInterceptors);
|
|
749
762
|
}
|
|
750
763
|
}
|
|
751
|
-
(0,
|
|
764
|
+
(0, import_immer2.enablePatches)();
|
|
752
765
|
function eventHandlerInterceptor(handler) {
|
|
753
766
|
return {
|
|
754
767
|
id: "fx-handler",
|
|
755
768
|
before(context) {
|
|
756
|
-
const
|
|
757
|
-
const event = coeffects.event;
|
|
769
|
+
const event = context.coeffects.event;
|
|
758
770
|
const params = event.slice(1);
|
|
759
771
|
let effects = [];
|
|
760
|
-
const [newDb, patches] = (0,
|
|
772
|
+
const [newDb, patches] = (0, import_immer2.produceWithPatches)(
|
|
761
773
|
getAppDb(),
|
|
762
774
|
(draftDb) => {
|
|
763
|
-
|
|
764
|
-
effects = handler(
|
|
775
|
+
const coeffectsWithDb = { ...context.coeffects, draftDb };
|
|
776
|
+
effects = handler(coeffectsWithDb, ...params) || [];
|
|
765
777
|
}
|
|
766
778
|
);
|
|
779
|
+
if (IS_DEV) {
|
|
780
|
+
try {
|
|
781
|
+
JSON.stringify(effects);
|
|
782
|
+
} catch (e) {
|
|
783
|
+
consoleLog("warn", `[reflex] Effects ${effects} contain Proxy (probably an Immer draft). Use current() for draftDb values.`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
767
786
|
context.newDb = newDb;
|
|
768
787
|
context.patches = patches;
|
|
769
788
|
mergeTrace({ tags: { "patches": patches, "effects": effects } });
|
|
@@ -808,11 +827,11 @@ function handle(eventV) {
|
|
|
808
827
|
function regEventErrorHandler(handler) {
|
|
809
828
|
registerHandler("error", "event-handler", handler);
|
|
810
829
|
}
|
|
811
|
-
function defaultErrorHandler(originalError,
|
|
830
|
+
function defaultErrorHandler(originalError, reflexError) {
|
|
812
831
|
consoleLog("error", "[reflex] Interceptor Exception:", {
|
|
813
832
|
originalError,
|
|
814
|
-
|
|
815
|
-
data:
|
|
833
|
+
reflexError,
|
|
834
|
+
data: reflexError.data
|
|
816
835
|
});
|
|
817
836
|
throw originalError;
|
|
818
837
|
}
|
|
@@ -1187,21 +1206,22 @@ function setupSubsHotReload() {
|
|
|
1187
1206
|
return { dispose, accept };
|
|
1188
1207
|
}
|
|
1189
1208
|
function HotReloadWrapper({ children }) {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
return import_react2.default.createElement(import_react2.default.Fragment, { key }, children);
|
|
1193
|
-
} else {
|
|
1194
|
-
return children;
|
|
1195
|
-
}
|
|
1209
|
+
const key = useHotReloadKey();
|
|
1210
|
+
return import_react2.default.createElement(import_react2.default.Fragment, { key }, children);
|
|
1196
1211
|
}
|
|
1197
1212
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1198
1213
|
0 && (module.exports = {
|
|
1214
|
+
DISPATCH,
|
|
1215
|
+
DISPATCH_LATER,
|
|
1199
1216
|
HotReloadWrapper,
|
|
1217
|
+
NOW,
|
|
1218
|
+
RANDOM,
|
|
1200
1219
|
clearGlobalInterceptors,
|
|
1201
1220
|
clearHandlers,
|
|
1202
1221
|
clearHotReloadCallbacks,
|
|
1203
1222
|
clearReactions,
|
|
1204
1223
|
clearSubs,
|
|
1224
|
+
current,
|
|
1205
1225
|
debounceAndDispatch,
|
|
1206
1226
|
defaultErrorHandler,
|
|
1207
1227
|
disableTracing,
|
|
@@ -1213,7 +1233,7 @@ function HotReloadWrapper({ children }) {
|
|
|
1213
1233
|
getHandler,
|
|
1214
1234
|
getSubscriptionValue,
|
|
1215
1235
|
initAppDb,
|
|
1216
|
-
|
|
1236
|
+
original,
|
|
1217
1237
|
regCoeffect,
|
|
1218
1238
|
regEffect,
|
|
1219
1239
|
regEvent,
|
|
@@ -1222,7 +1242,6 @@ function HotReloadWrapper({ children }) {
|
|
|
1222
1242
|
regSub,
|
|
1223
1243
|
registerHotReloadCallback,
|
|
1224
1244
|
registerTraceCb,
|
|
1225
|
-
setDebugEnabled,
|
|
1226
1245
|
setupSubsHotReload,
|
|
1227
1246
|
throttleAndDispatch,
|
|
1228
1247
|
triggerHotReload,
|
package/dist/index.d.cts
CHANGED
|
@@ -7,7 +7,7 @@ type EventVector = [Id, ...any[]];
|
|
|
7
7
|
type EventHandler<T = Record<string, any>> = (coeffects: CoEffects<T>, ...params: any[]) => Effects | void;
|
|
8
8
|
type EffectHandler = (value: any) => void;
|
|
9
9
|
type CoEffectHandler<T = Record<string, any>> = (coeffects: CoEffects<T>, value?: any) => CoEffects<T>;
|
|
10
|
-
type ErrorHandler = (originalError: Error,
|
|
10
|
+
type ErrorHandler = (originalError: Error, reflexError: Error & {
|
|
11
11
|
data: any;
|
|
12
12
|
}) => void;
|
|
13
13
|
type SubVector = [Id, ...any[]];
|
|
@@ -42,6 +42,23 @@ interface Interceptor<T = Record<string, any>> {
|
|
|
42
42
|
declare function initAppDb<T = Record<string, any>>(value: Db<T>): void;
|
|
43
43
|
declare function getAppDb<T = Record<string, any>>(): Db<T>;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Safe versions of immer's original and current functions
|
|
47
|
+
* These check if the value is actually a draft before calling the immer functions
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Safe version of immer's original function
|
|
51
|
+
* Returns the original (frozen) version of a draft if the value is a draft,
|
|
52
|
+
* otherwise returns the value as-is
|
|
53
|
+
*/
|
|
54
|
+
declare function original<T>(value: T): T;
|
|
55
|
+
/**
|
|
56
|
+
* Safe version of immer's current function
|
|
57
|
+
* Returns the current draft state as a plain object if the value is a draft,
|
|
58
|
+
* otherwise returns the value as-is
|
|
59
|
+
*/
|
|
60
|
+
declare function current<T>(value: T): T;
|
|
61
|
+
|
|
45
62
|
/** Register an event handler with only a handler function (db event) */
|
|
46
63
|
declare function regEvent<T = Record<string, any>>(id: Id, handler: EventHandler<T>): void;
|
|
47
64
|
/** Register an event handler with interceptors and handler function (backward compatibility) */
|
|
@@ -57,16 +74,16 @@ declare function regEvent<T = Record<string, any>>(id: Id, handler: EventHandler
|
|
|
57
74
|
* Only one handler can be registered. Registering a new handler clears the existing handler.
|
|
58
75
|
*
|
|
59
76
|
* This handler function has the signature:
|
|
60
|
-
* `(originalError: Error,
|
|
77
|
+
* `(originalError: Error, reflexError: Error & { data: any }) => void`
|
|
61
78
|
*
|
|
62
79
|
* - `originalError`: A platform-native Error object.
|
|
63
80
|
* Represents the original error thrown by user code.
|
|
64
81
|
* This is the error you see when no handler is registered.
|
|
65
82
|
*
|
|
66
|
-
* - `
|
|
83
|
+
* - `reflexError`: An Error object with additional data.
|
|
67
84
|
* Includes the stacktrace of reflex's internal functions,
|
|
68
85
|
* and extra data about the interceptor process.
|
|
69
|
-
* Access `
|
|
86
|
+
* Access `reflexError.data` to get this info.
|
|
70
87
|
*
|
|
71
88
|
* The data includes:
|
|
72
89
|
* - `interceptor`: the `id` of the throwing interceptor.
|
|
@@ -77,7 +94,7 @@ declare function regEventErrorHandler(handler: ErrorHandler): void;
|
|
|
77
94
|
/**
|
|
78
95
|
* Default error handler that logs errors to console
|
|
79
96
|
*/
|
|
80
|
-
declare function defaultErrorHandler(originalError: Error,
|
|
97
|
+
declare function defaultErrorHandler(originalError: Error, reflexError: Error & {
|
|
81
98
|
data: any;
|
|
82
99
|
}): void;
|
|
83
100
|
|
|
@@ -85,8 +102,12 @@ declare function regSub<R>(id: Id, computeFn?: (...values: any[]) => R, depsFn?:
|
|
|
85
102
|
declare function getSubscriptionValue<T>(subVector: SubVector): T;
|
|
86
103
|
|
|
87
104
|
declare function regEffect(id: string, handler: EffectHandler): void;
|
|
105
|
+
declare const DISPATCH_LATER = "dispatch-later";
|
|
106
|
+
declare const DISPATCH = "dispatch";
|
|
88
107
|
|
|
89
108
|
declare function regCoeffect(id: string, handler: CoEffectHandler): void;
|
|
109
|
+
declare const NOW = "now";
|
|
110
|
+
declare const RANDOM = "random";
|
|
90
111
|
|
|
91
112
|
/**
|
|
92
113
|
* Register a global interceptor
|
|
@@ -101,14 +122,6 @@ declare function getGlobalInterceptors(): Interceptor[];
|
|
|
101
122
|
*/
|
|
102
123
|
declare function clearGlobalInterceptors(): void;
|
|
103
124
|
declare function clearGlobalInterceptors(id: string): void;
|
|
104
|
-
/**
|
|
105
|
-
* Enable or disable debug mode
|
|
106
|
-
*/
|
|
107
|
-
declare function setDebugEnabled(enabled: boolean): void;
|
|
108
|
-
/**
|
|
109
|
-
* Check if debug mode is enabled
|
|
110
|
-
*/
|
|
111
|
-
declare function isDebugEnabled(): boolean;
|
|
112
125
|
|
|
113
126
|
type Kind = 'event' | 'fx' | 'cofx' | 'sub' | 'subDeps' | 'error';
|
|
114
127
|
type RegistryHandler = EventHandler | EffectHandler | CoEffectHandler | ErrorHandler | SubHandler | SubDepsHandler;
|
|
@@ -179,9 +192,9 @@ declare function setupSubsHotReload(): {
|
|
|
179
192
|
*/
|
|
180
193
|
declare function HotReloadWrapper({ children }: {
|
|
181
194
|
children: React.ReactNode;
|
|
182
|
-
}):
|
|
195
|
+
}): React.FunctionComponentElement<{
|
|
183
196
|
children?: React.ReactNode | undefined;
|
|
184
|
-
}
|
|
197
|
+
}>;
|
|
185
198
|
|
|
186
199
|
type TraceID = number;
|
|
187
200
|
interface TraceOpts {
|
|
@@ -202,4 +215,4 @@ declare function disableTracing(): void;
|
|
|
202
215
|
declare function registerTraceCb(key: string, cb: TraceCallback): void;
|
|
203
216
|
declare function enableTracePrint(): void;
|
|
204
217
|
|
|
205
|
-
export { CoEffectHandler, CoEffects, Context, Db, DispatchLaterEffect, EffectHandler, Effects, ErrorHandler, EventHandler, EventVector, HotReloadWrapper, Id, Interceptor, SubVector, clearGlobalInterceptors, clearHandlers, clearHotReloadCallbacks, clearReactions, clearSubs, debounceAndDispatch, defaultErrorHandler, disableTracing, dispatch, enableTracePrint, enableTracing, getAppDb, getGlobalInterceptors, getHandler, getSubscriptionValue, initAppDb,
|
|
218
|
+
export { CoEffectHandler, CoEffects, Context, DISPATCH, DISPATCH_LATER, Db, DispatchLaterEffect, EffectHandler, Effects, ErrorHandler, EventHandler, EventVector, HotReloadWrapper, Id, Interceptor, NOW, RANDOM, SubVector, clearGlobalInterceptors, clearHandlers, clearHotReloadCallbacks, clearReactions, clearSubs, current, debounceAndDispatch, defaultErrorHandler, disableTracing, dispatch, enableTracePrint, enableTracing, getAppDb, getGlobalInterceptors, getHandler, getSubscriptionValue, initAppDb, original, regCoeffect, regEffect, regEvent, regEventErrorHandler, regGlobalInterceptor, regSub, registerHotReloadCallback, registerTraceCb, setupSubsHotReload, throttleAndDispatch, triggerHotReload, useHotReload, useHotReloadKey, useSubscription };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ type EventVector = [Id, ...any[]];
|
|
|
7
7
|
type EventHandler<T = Record<string, any>> = (coeffects: CoEffects<T>, ...params: any[]) => Effects | void;
|
|
8
8
|
type EffectHandler = (value: any) => void;
|
|
9
9
|
type CoEffectHandler<T = Record<string, any>> = (coeffects: CoEffects<T>, value?: any) => CoEffects<T>;
|
|
10
|
-
type ErrorHandler = (originalError: Error,
|
|
10
|
+
type ErrorHandler = (originalError: Error, reflexError: Error & {
|
|
11
11
|
data: any;
|
|
12
12
|
}) => void;
|
|
13
13
|
type SubVector = [Id, ...any[]];
|
|
@@ -42,6 +42,23 @@ interface Interceptor<T = Record<string, any>> {
|
|
|
42
42
|
declare function initAppDb<T = Record<string, any>>(value: Db<T>): void;
|
|
43
43
|
declare function getAppDb<T = Record<string, any>>(): Db<T>;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Safe versions of immer's original and current functions
|
|
47
|
+
* These check if the value is actually a draft before calling the immer functions
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Safe version of immer's original function
|
|
51
|
+
* Returns the original (frozen) version of a draft if the value is a draft,
|
|
52
|
+
* otherwise returns the value as-is
|
|
53
|
+
*/
|
|
54
|
+
declare function original<T>(value: T): T;
|
|
55
|
+
/**
|
|
56
|
+
* Safe version of immer's current function
|
|
57
|
+
* Returns the current draft state as a plain object if the value is a draft,
|
|
58
|
+
* otherwise returns the value as-is
|
|
59
|
+
*/
|
|
60
|
+
declare function current<T>(value: T): T;
|
|
61
|
+
|
|
45
62
|
/** Register an event handler with only a handler function (db event) */
|
|
46
63
|
declare function regEvent<T = Record<string, any>>(id: Id, handler: EventHandler<T>): void;
|
|
47
64
|
/** Register an event handler with interceptors and handler function (backward compatibility) */
|
|
@@ -57,16 +74,16 @@ declare function regEvent<T = Record<string, any>>(id: Id, handler: EventHandler
|
|
|
57
74
|
* Only one handler can be registered. Registering a new handler clears the existing handler.
|
|
58
75
|
*
|
|
59
76
|
* This handler function has the signature:
|
|
60
|
-
* `(originalError: Error,
|
|
77
|
+
* `(originalError: Error, reflexError: Error & { data: any }) => void`
|
|
61
78
|
*
|
|
62
79
|
* - `originalError`: A platform-native Error object.
|
|
63
80
|
* Represents the original error thrown by user code.
|
|
64
81
|
* This is the error you see when no handler is registered.
|
|
65
82
|
*
|
|
66
|
-
* - `
|
|
83
|
+
* - `reflexError`: An Error object with additional data.
|
|
67
84
|
* Includes the stacktrace of reflex's internal functions,
|
|
68
85
|
* and extra data about the interceptor process.
|
|
69
|
-
* Access `
|
|
86
|
+
* Access `reflexError.data` to get this info.
|
|
70
87
|
*
|
|
71
88
|
* The data includes:
|
|
72
89
|
* - `interceptor`: the `id` of the throwing interceptor.
|
|
@@ -77,7 +94,7 @@ declare function regEventErrorHandler(handler: ErrorHandler): void;
|
|
|
77
94
|
/**
|
|
78
95
|
* Default error handler that logs errors to console
|
|
79
96
|
*/
|
|
80
|
-
declare function defaultErrorHandler(originalError: Error,
|
|
97
|
+
declare function defaultErrorHandler(originalError: Error, reflexError: Error & {
|
|
81
98
|
data: any;
|
|
82
99
|
}): void;
|
|
83
100
|
|
|
@@ -85,8 +102,12 @@ declare function regSub<R>(id: Id, computeFn?: (...values: any[]) => R, depsFn?:
|
|
|
85
102
|
declare function getSubscriptionValue<T>(subVector: SubVector): T;
|
|
86
103
|
|
|
87
104
|
declare function regEffect(id: string, handler: EffectHandler): void;
|
|
105
|
+
declare const DISPATCH_LATER = "dispatch-later";
|
|
106
|
+
declare const DISPATCH = "dispatch";
|
|
88
107
|
|
|
89
108
|
declare function regCoeffect(id: string, handler: CoEffectHandler): void;
|
|
109
|
+
declare const NOW = "now";
|
|
110
|
+
declare const RANDOM = "random";
|
|
90
111
|
|
|
91
112
|
/**
|
|
92
113
|
* Register a global interceptor
|
|
@@ -101,14 +122,6 @@ declare function getGlobalInterceptors(): Interceptor[];
|
|
|
101
122
|
*/
|
|
102
123
|
declare function clearGlobalInterceptors(): void;
|
|
103
124
|
declare function clearGlobalInterceptors(id: string): void;
|
|
104
|
-
/**
|
|
105
|
-
* Enable or disable debug mode
|
|
106
|
-
*/
|
|
107
|
-
declare function setDebugEnabled(enabled: boolean): void;
|
|
108
|
-
/**
|
|
109
|
-
* Check if debug mode is enabled
|
|
110
|
-
*/
|
|
111
|
-
declare function isDebugEnabled(): boolean;
|
|
112
125
|
|
|
113
126
|
type Kind = 'event' | 'fx' | 'cofx' | 'sub' | 'subDeps' | 'error';
|
|
114
127
|
type RegistryHandler = EventHandler | EffectHandler | CoEffectHandler | ErrorHandler | SubHandler | SubDepsHandler;
|
|
@@ -179,9 +192,9 @@ declare function setupSubsHotReload(): {
|
|
|
179
192
|
*/
|
|
180
193
|
declare function HotReloadWrapper({ children }: {
|
|
181
194
|
children: React.ReactNode;
|
|
182
|
-
}):
|
|
195
|
+
}): React.FunctionComponentElement<{
|
|
183
196
|
children?: React.ReactNode | undefined;
|
|
184
|
-
}
|
|
197
|
+
}>;
|
|
185
198
|
|
|
186
199
|
type TraceID = number;
|
|
187
200
|
interface TraceOpts {
|
|
@@ -202,4 +215,4 @@ declare function disableTracing(): void;
|
|
|
202
215
|
declare function registerTraceCb(key: string, cb: TraceCallback): void;
|
|
203
216
|
declare function enableTracePrint(): void;
|
|
204
217
|
|
|
205
|
-
export { CoEffectHandler, CoEffects, Context, Db, DispatchLaterEffect, EffectHandler, Effects, ErrorHandler, EventHandler, EventVector, HotReloadWrapper, Id, Interceptor, SubVector, clearGlobalInterceptors, clearHandlers, clearHotReloadCallbacks, clearReactions, clearSubs, debounceAndDispatch, defaultErrorHandler, disableTracing, dispatch, enableTracePrint, enableTracing, getAppDb, getGlobalInterceptors, getHandler, getSubscriptionValue, initAppDb,
|
|
218
|
+
export { CoEffectHandler, CoEffects, Context, DISPATCH, DISPATCH_LATER, Db, DispatchLaterEffect, EffectHandler, Effects, ErrorHandler, EventHandler, EventVector, HotReloadWrapper, Id, Interceptor, NOW, RANDOM, SubVector, clearGlobalInterceptors, clearHandlers, clearHotReloadCallbacks, clearReactions, clearSubs, current, debounceAndDispatch, defaultErrorHandler, disableTracing, dispatch, enableTracePrint, enableTracing, getAppDb, getGlobalInterceptors, getHandler, getSubscriptionValue, initAppDb, original, regCoeffect, regEffect, regEvent, regEventErrorHandler, regGlobalInterceptor, regSub, registerHotReloadCallback, registerTraceCb, setupSubsHotReload, throttleAndDispatch, triggerHotReload, useHotReload, useHotReloadKey, useSubscription };
|
package/dist/index.mjs
CHANGED
|
@@ -158,6 +158,15 @@ function updateAppDbWithPatches(newDb, patches) {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
// src/immer-utils.ts
|
|
162
|
+
import { isDraft, original as immerOriginal, current as immerCurrent } from "immer";
|
|
163
|
+
function original(value) {
|
|
164
|
+
return isDraft(value) ? immerOriginal(value) : value;
|
|
165
|
+
}
|
|
166
|
+
function current(value) {
|
|
167
|
+
return isDraft(value) ? immerCurrent(value) : value;
|
|
168
|
+
}
|
|
169
|
+
|
|
161
170
|
// src/interceptor.ts
|
|
162
171
|
function isInterceptor(m) {
|
|
163
172
|
if (typeof m !== "object" || m === null)
|
|
@@ -246,8 +255,8 @@ function execute(eventV, interceptors) {
|
|
|
246
255
|
try {
|
|
247
256
|
return executeInterceptors(ctx);
|
|
248
257
|
} catch (e) {
|
|
249
|
-
const
|
|
250
|
-
errorHandler(e.cause || e,
|
|
258
|
+
const reflexError = mergeExData(e, { eventV });
|
|
259
|
+
errorHandler(e.cause || e, reflexError);
|
|
251
260
|
return ctx;
|
|
252
261
|
}
|
|
253
262
|
}
|
|
@@ -275,11 +284,13 @@ function getInjectCofxInterceptor(id, value) {
|
|
|
275
284
|
}
|
|
276
285
|
};
|
|
277
286
|
}
|
|
278
|
-
|
|
287
|
+
var NOW = "now";
|
|
288
|
+
var RANDOM = "random";
|
|
289
|
+
regCoeffect(NOW, (coeffects) => ({
|
|
279
290
|
...coeffects,
|
|
280
291
|
now: Date.now()
|
|
281
292
|
}));
|
|
282
|
-
regCoeffect(
|
|
293
|
+
regCoeffect(RANDOM, (coeffects) => ({
|
|
283
294
|
...coeffects,
|
|
284
295
|
random: Math.random()
|
|
285
296
|
}));
|
|
@@ -464,6 +475,8 @@ var doFxInterceptor = {
|
|
|
464
475
|
return context;
|
|
465
476
|
}
|
|
466
477
|
};
|
|
478
|
+
var DISPATCH_LATER = "dispatch-later";
|
|
479
|
+
var DISPATCH = "dispatch";
|
|
467
480
|
function dispatchLater(effect) {
|
|
468
481
|
const { ms, dispatch: eventToDispatch } = effect;
|
|
469
482
|
if (!Array.isArray(eventToDispatch) || typeof ms !== "number") {
|
|
@@ -475,10 +488,10 @@ function dispatchLater(effect) {
|
|
|
475
488
|
}
|
|
476
489
|
setTimeout(() => dispatch(eventToDispatch), Math.max(0, ms));
|
|
477
490
|
}
|
|
478
|
-
regEffect(
|
|
491
|
+
regEffect(DISPATCH_LATER, (value) => {
|
|
479
492
|
dispatchLater(value);
|
|
480
493
|
});
|
|
481
|
-
regEffect(
|
|
494
|
+
regEffect(DISPATCH, (value) => {
|
|
482
495
|
if (!Array.isArray(value)) {
|
|
483
496
|
consoleLog("error", "[reflex] ignoring bad dispatch value. Expected a vector, but got:", value);
|
|
484
497
|
return;
|
|
@@ -491,16 +504,11 @@ import { enablePatches, produceWithPatches } from "immer";
|
|
|
491
504
|
|
|
492
505
|
// src/settings.ts
|
|
493
506
|
var store = {
|
|
494
|
-
globalInterceptors: []
|
|
495
|
-
debugEnabled: true
|
|
496
|
-
// Default to true, can be configured via setDebugEnabled
|
|
507
|
+
globalInterceptors: []
|
|
497
508
|
};
|
|
498
509
|
function replaceGlobalInterceptor(globalInterceptors, interceptor) {
|
|
499
510
|
return globalInterceptors.reduce((ret, existingInterceptor) => {
|
|
500
511
|
if (interceptor.id === existingInterceptor.id) {
|
|
501
|
-
if (store.debugEnabled) {
|
|
502
|
-
consoleLog("warn", "[reflex] replacing duplicate global interceptor id:", interceptor.id);
|
|
503
|
-
}
|
|
504
512
|
return [...ret, interceptor];
|
|
505
513
|
} else {
|
|
506
514
|
return [...ret, existingInterceptor];
|
|
@@ -526,12 +534,6 @@ function clearGlobalInterceptors(id) {
|
|
|
526
534
|
store.globalInterceptors = store.globalInterceptors.filter((interceptor) => interceptor.id !== id);
|
|
527
535
|
}
|
|
528
536
|
}
|
|
529
|
-
function setDebugEnabled(enabled) {
|
|
530
|
-
store.debugEnabled = enabled;
|
|
531
|
-
}
|
|
532
|
-
function isDebugEnabled() {
|
|
533
|
-
return store.debugEnabled;
|
|
534
|
-
}
|
|
535
537
|
|
|
536
538
|
// src/trace.ts
|
|
537
539
|
var nextId = 1;
|
|
@@ -632,6 +634,13 @@ function enableTracePrint() {
|
|
|
632
634
|
});
|
|
633
635
|
}
|
|
634
636
|
|
|
637
|
+
// src/env.ts
|
|
638
|
+
var IS_DEV = (
|
|
639
|
+
// Node.js check
|
|
640
|
+
typeof process !== "undefined" && process.env?.NODE_ENV === "development" || // React Native / bundler check
|
|
641
|
+
typeof __DEV__ !== "undefined" && __DEV__
|
|
642
|
+
);
|
|
643
|
+
|
|
635
644
|
// src/events.ts
|
|
636
645
|
var KIND3 = "event";
|
|
637
646
|
function regEvent(id, handler, cofxOrInterceptors, interceptors) {
|
|
@@ -685,17 +694,23 @@ function eventHandlerInterceptor(handler) {
|
|
|
685
694
|
return {
|
|
686
695
|
id: "fx-handler",
|
|
687
696
|
before(context) {
|
|
688
|
-
const
|
|
689
|
-
const event = coeffects.event;
|
|
697
|
+
const event = context.coeffects.event;
|
|
690
698
|
const params = event.slice(1);
|
|
691
699
|
let effects = [];
|
|
692
700
|
const [newDb, patches] = produceWithPatches(
|
|
693
701
|
getAppDb(),
|
|
694
702
|
(draftDb) => {
|
|
695
|
-
|
|
696
|
-
effects = handler(
|
|
703
|
+
const coeffectsWithDb = { ...context.coeffects, draftDb };
|
|
704
|
+
effects = handler(coeffectsWithDb, ...params) || [];
|
|
697
705
|
}
|
|
698
706
|
);
|
|
707
|
+
if (IS_DEV) {
|
|
708
|
+
try {
|
|
709
|
+
JSON.stringify(effects);
|
|
710
|
+
} catch (e) {
|
|
711
|
+
consoleLog("warn", `[reflex] Effects ${effects} contain Proxy (probably an Immer draft). Use current() for draftDb values.`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
699
714
|
context.newDb = newDb;
|
|
700
715
|
context.patches = patches;
|
|
701
716
|
mergeTrace({ tags: { "patches": patches, "effects": effects } });
|
|
@@ -740,11 +755,11 @@ function handle(eventV) {
|
|
|
740
755
|
function regEventErrorHandler(handler) {
|
|
741
756
|
registerHandler("error", "event-handler", handler);
|
|
742
757
|
}
|
|
743
|
-
function defaultErrorHandler(originalError,
|
|
758
|
+
function defaultErrorHandler(originalError, reflexError) {
|
|
744
759
|
consoleLog("error", "[reflex] Interceptor Exception:", {
|
|
745
760
|
originalError,
|
|
746
|
-
|
|
747
|
-
data:
|
|
761
|
+
reflexError,
|
|
762
|
+
data: reflexError.data
|
|
748
763
|
});
|
|
749
764
|
throw originalError;
|
|
750
765
|
}
|
|
@@ -1119,20 +1134,21 @@ function setupSubsHotReload() {
|
|
|
1119
1134
|
return { dispose, accept };
|
|
1120
1135
|
}
|
|
1121
1136
|
function HotReloadWrapper({ children }) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
return React.createElement(React.Fragment, { key }, children);
|
|
1125
|
-
} else {
|
|
1126
|
-
return children;
|
|
1127
|
-
}
|
|
1137
|
+
const key = useHotReloadKey();
|
|
1138
|
+
return React.createElement(React.Fragment, { key }, children);
|
|
1128
1139
|
}
|
|
1129
1140
|
export {
|
|
1141
|
+
DISPATCH,
|
|
1142
|
+
DISPATCH_LATER,
|
|
1130
1143
|
HotReloadWrapper,
|
|
1144
|
+
NOW,
|
|
1145
|
+
RANDOM,
|
|
1131
1146
|
clearGlobalInterceptors,
|
|
1132
1147
|
clearHandlers,
|
|
1133
1148
|
clearHotReloadCallbacks,
|
|
1134
1149
|
clearReactions,
|
|
1135
1150
|
clearSubs,
|
|
1151
|
+
current,
|
|
1136
1152
|
debounceAndDispatch,
|
|
1137
1153
|
defaultErrorHandler,
|
|
1138
1154
|
disableTracing,
|
|
@@ -1144,7 +1160,7 @@ export {
|
|
|
1144
1160
|
getHandler,
|
|
1145
1161
|
getSubscriptionValue,
|
|
1146
1162
|
initAppDb,
|
|
1147
|
-
|
|
1163
|
+
original,
|
|
1148
1164
|
regCoeffect,
|
|
1149
1165
|
regEffect,
|
|
1150
1166
|
regEvent,
|
|
@@ -1153,7 +1169,6 @@ export {
|
|
|
1153
1169
|
regSub,
|
|
1154
1170
|
registerHotReloadCallback,
|
|
1155
1171
|
registerTraceCb,
|
|
1156
|
-
setDebugEnabled,
|
|
1157
1172
|
setupSubsHotReload,
|
|
1158
1173
|
throttleAndDispatch,
|
|
1159
1174
|
triggerHotReload,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flexsurfer/reflex",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"dev": "tsup --watch",
|
|
31
31
|
"test": "jest --passWithNoTests",
|
|
32
32
|
"test:clean": "jest --passWithNoTests --silent",
|
|
33
|
-
"test:watch": "jest --passWithNoTests --watch"
|
|
33
|
+
"test:watch": "jest --passWithNoTests --watch",
|
|
34
|
+
"test:ci": "jest --passWithNoTests --coverage --silent",
|
|
35
|
+
"prepublishOnly": "npm run test:ci && npm run build"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@testing-library/dom": "^10.4.0",
|