reca 2.4.8 → 2.4.11

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 (2) hide show
  1. package/README.md +3 -399
  2. package/package.json +9 -11
package/README.md CHANGED
@@ -1,401 +1,5 @@
1
- # ReCA - React Clean Architecture state manager
1
+ # ReCA
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/reca.svg?style=flat)](https://www.npmjs.com/package/reca)
4
- [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/LabEG/reca/blob/main/LICENSE)
5
- ![npm downloads](https://img.shields.io/npm/dm/reca.svg)
6
- [![Codacy Badge](https://app.codacy.com/project/badge/Grade/e9e573d8408945168d14d83c81a103e6)](https://www.codacy.com/gh/LabEG/reca/dashboard?utm_source=github.com&utm_medium=referral&utm_content=LabEG/reca&utm_campaign=Badge_Grade)
7
- ![build status](https://github.com/LabEG/reca/workflows/Test%20Pull%20Request/badge.svg)
8
- [![CodeQL](https://github.com/LabEG/reca/workflows/CodeQL%20Advanced/badge.svg)](https://github.com/LabEG/reca/security/code-scanning)
9
- [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
3
+ > **Note:** This file is replaced by the README.md from the repository root during npm publishing.
10
4
 
11
- Created at the intersection of Functional style and OOP technologies. It is based on the simplicity of the functional style of the view, enriched with OOP technologies for writing business logic. Perfect for beginner developers and complex enterprise applications
12
-
13
- ## Features
14
-
15
- - **Microstores** - calculations state of components don't affect to other components, small CPU usage for update states,
16
- - **Direct Functions Call** - don't need heavy CPU utilization for search function in reducer, just call the function directly,
17
- - **No Boilerplate** - write only business code without those debt,
18
- - **Dependency Injection** - override any part of your application for unit test or other customer,
19
- - **Microfrontend** - perfect support microfrontends out the box without any boilerplates,
20
- - **Simple Data Flow** - don't need search functions call chain for debug your reducers,
21
- - **Code Organization** - structures the code easily even for large enterprise applications,
22
- - **Extra Small Size** - only 1kb of minified code.
23
-
24
- ## Comparison with Other Libraries
25
-
26
- | Feature | ReCA | Zustand | MobX | Redux |
27
- |---------|------|----------|------|-------|
28
- | **Bundle Size** | ~1KB | ~1KB | ~16KB | ~8KB |
29
- | **Boilerplate** | Minimal | Minimal | Medium | Heavy |
30
- | **Learning Curve** | Easy | Easy | Medium | Steep |
31
- | **TypeScript** | Built-in | Good | Good | Good |
32
- | **Performance** | Excellent | Excellent | Excellent | Good |
33
- | **Dependency Injection** | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ Manual |
34
- | **Clean Architecture** | ✅ Native | ❌ Limited | ⚠️ Requires setup | ⚠️ Requires setup |
35
- | **Microstores** | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Monostore |
36
- | **SSR Support** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
37
- | **Middleware** | Via DI | ✅ Yes | ❌ Limited | ✅ Yes |
38
- | **Async Actions** | ✅ Native | ✅ Native | ✅ Native | ⚠️ Requires thunk/saga |
39
-
40
- ### Why Choose ReCA?
41
-
42
- - **Smallest footprint** - Only 1KB minified, same as Zustand but with more features
43
- - **Zero boilerplate** - No actions, reducers, or dispatchers needed
44
- - **Enterprise-ready** - Built-in DI and Clean Architecture support
45
- - **Developer-friendly** - Simple API, easy to learn and use
46
- - **Flexible** - Choose between AutoStore (automatic) or Store (manual control)
47
- - **Type-safe** - Full TypeScript support out of the box
48
-
49
- ## Installation
50
-
51
- ### Using npm
52
-
53
- ```bash
54
- npm install reca reflect-metadata
55
- ```
56
-
57
- ### Using yarn
58
-
59
- ```bash
60
- yarn add reca reflect-metadata
61
- ```
62
-
63
- ### Using pnpm
64
-
65
- ```bash
66
- pnpm add reca reflect-metadata
67
- ```
68
-
69
- ### Setup
70
-
71
- After installation, import `reflect-metadata` at the entry point of your application (e.g., `index.tsx` or `main.tsx`):
72
-
73
- ```typescript
74
- import "reflect-metadata";
75
- import React from "react";
76
- import ReactDOM from "react-dom/client";
77
- import { App } from "./App";
78
-
79
- ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
80
- ```
81
-
82
- > **Note:** The `reflect-metadata` package is required for dependency injection functionality. Make sure to import it before any other imports in your application entry point.
83
-
84
- ## Examples
85
-
86
- ### Quick Start - Counter Example
87
-
88
- A simple example to get you started with ReCA:
89
-
90
- ```typescript
91
- // counter.store.ts
92
- import { AutoStore } from "reca";
93
-
94
- export class CounterStore extends AutoStore {
95
- public count: number = 0;
96
-
97
- public increment(): void {
98
- this.count++;
99
- }
100
-
101
- public decrement(): void {
102
- this.count--;
103
- }
104
- }
105
-
106
- // Counter.tsx
107
- import { useStore } from "reca";
108
- import { CounterStore } from "./stores/counter.store";
109
-
110
- export const Counter = () => {
111
- const store = useStore(CounterStore);
112
-
113
- return (
114
- <div>
115
- <h1>Count: {store.count}</h1>
116
- <button onClick={store.increment}>+1</button>
117
- <button onClick={store.decrement}>-1</button>
118
- </div>
119
- );
120
- };
121
- ```
122
-
123
- ### ToDo Example
124
-
125
- Create your Store by inheriting from AutoStore, and use it in a component via useStore hook.
126
-
127
- ``` typescript
128
- // todo.store.ts
129
- import {AutoStore} from "reca";
130
- import type {FormEvent} from "react";
131
-
132
- export class ToDoStore extends AutoStore {
133
-
134
- public currentTodo: string = "";
135
-
136
- public todos: string[] = [];
137
-
138
- public handleAddTodo (): void {
139
- this.todos.push(this.currentTodo);
140
- }
141
-
142
- public handleDeleteTodo (index: number): void {
143
- this.todos.splice(index, 1);
144
- }
145
-
146
- public handleCurrentEdit (event: FormEvent<HTMLInputElement>): void {
147
- this.currentTodo = event.currentTarget.value;
148
- }
149
-
150
- }
151
-
152
-
153
- // todo.component.ts
154
- import {useStore} from "reca";
155
- import {ToDoStore} from "../stores/todo.store";
156
-
157
- export const ToDoComponent = (): JSX.Element => {
158
- const store = useStore(ToDoStore);
159
-
160
- return (
161
- <div className="todos">
162
- <div className="todos-list">
163
- {
164
- store.todos.map((todo, index) => (
165
- <div className="todo">
166
- {todo}
167
-
168
- <button
169
- className="todo-delete"
170
- onClick={() => store.handleDeleteTodo(index)}
171
- type="button"
172
- >
173
- X
174
- </button>
175
- </div>
176
- ))
177
- }
178
- </div>
179
-
180
- <div className="todos-input">
181
- <input
182
- onInput={store.handleCurrentEdit}
183
- value={store.currentTodo}
184
- />
185
-
186
- <button
187
- onClick={store.handleAddTodo}
188
- type="button"
189
- >
190
- add
191
- </button>
192
- </div>
193
- </div>
194
- );
195
- };
196
- ```
197
-
198
- ### Example low-level Store
199
-
200
- Also, if you need uncompromising performance, you can use the low-level Store. But you will need to start redrawing manually using the `this.redraw()` method. Also you must pass arrow function to all used HTMLElement events, such as onClick.
201
-
202
- ``` typescript
203
- // todo.store.ts
204
- import {Store} from "reca";
205
- import type {FormEvent} from "react";
206
-
207
- export class ToDoStore extends Store {
208
-
209
- public currentTodo: string = "";
210
-
211
- public todos: string[] = [];
212
-
213
- public handleAddTodo (): void {
214
- this.todos.push(this.currentTodo);
215
- this.redraw();
216
- }
217
-
218
- public handleDeleteTodo (index: number): void {
219
- this.todos.splice(index, 1);
220
- this.redraw();
221
- }
222
-
223
- public handleCurrentEdit (event: FormEvent<HTMLInputElement>): void {
224
- this.currentTodo = event.currentTarget.value;
225
- this.redraw();
226
- }
227
- }
228
-
229
-
230
- // todo.component.ts
231
- import {useStore} from "reca";
232
- import {ToDoStore} from "../stores/todo.store";
233
-
234
- export const ToDoComponent = (): JSX.Element => {
235
- const store = useStore(ToDoStore);
236
-
237
- return (
238
- <div className="todos">
239
- ...
240
-
241
- <div className="todos-input">
242
- <input
243
- onInput={() => store.handleCurrentEdit()}
244
- value={store.currentTodo}
245
- />
246
-
247
- <button
248
- onClick={() => store.handleAddTodo()}
249
- type="button"
250
- >
251
- add
252
- </button>
253
- </div>
254
- </div>
255
- );
256
- };
257
- ```
258
-
259
- ### Advanced Example - Dependency Injection for Enterprise Applications
260
-
261
- This example demonstrates how to build scalable enterprise applications using ReCA with Dependency Injection. It shows the simplicity of business logic organization following Clean Architecture principles.
262
-
263
- The example includes:
264
-
265
- - **Service Layer** - encapsulates business logic and external API calls
266
- - **Model Layer** - defines data structures
267
- - **Store Layer** - manages state and coordinates services
268
- - **Component Layer** - pure view logic
269
-
270
- This architecture makes your code:
271
-
272
- - **Testable** - easily mock services for unit tests
273
- - **Maintainable** - clear separation of concerns
274
- - **Scalable** - add new features without modifying existing code
275
- - **Flexible** - swap implementations through DI (e.g., Repository, Provider, Logger)
276
-
277
- ```typescript
278
- // SpaceXCompanyInfo.ts
279
- export class SpaceXCompanyInfo {
280
-
281
- public name: string = "";
282
-
283
- public founder: string = "";
284
-
285
- public employees: number = 0;
286
-
287
- public applyData (json: object): this {
288
- Object.assign(this, json);
289
- return this;
290
- }
291
-
292
- }
293
-
294
-
295
- // SpaceXService.ts
296
- import {reflection} from "first-di";
297
- import {SpaceXCompanyInfo} from "../models/SpaceXCompanyInfo";
298
-
299
- @reflection
300
- export class SpaceXService {
301
-
302
- public async getCompanyInfo (): Promise<SpaceXCompanyInfo> {
303
- const response = await fetch("https://api.spacexdata.com/v3/info");
304
- const json: unknown = await response.json();
305
-
306
- // ... and manies manies lines of logics
307
-
308
- if (typeof json === "object" && json !== null) {
309
- return new SpaceXCompanyInfo().applyData(json);
310
- }
311
- throw new Error("SpaceXService.getCompanyInfo: response object is not json");
312
- }
313
-
314
- }
315
-
316
-
317
- // SpaceXStore.ts
318
- import {reflection} from "first-di";
319
- import {AutoStore} from "reca";
320
- import {SpaceXCompanyInfo} from "../models/SpaceXCompanyInfo.js";
321
- import {SpaceXService} from "../services/SpaceXService.js";
322
-
323
- @reflection
324
- export class SpaceXStore extends AutoStore {
325
-
326
- public companyInfo: SpaceXCompanyInfo = new SpaceXCompanyInfo();
327
-
328
- public constructor (
329
- private readonly spaceXService: SpaceXService,
330
- // private readonly logger: Logger
331
- ) {
332
- super();
333
- }
334
-
335
- public activate (): void {
336
- this.fetchCompanyInfo();
337
- }
338
-
339
- private async fetchCompanyInfo (): Promise<void> {
340
- try {
341
- this.companyInfo = await this.spaceXService.getCompanyInfo();
342
- } catch (error) {
343
- // Process exceptions, ex: this.logger.error(error.message);
344
- }
345
- }
346
-
347
- }
348
-
349
-
350
- // SpaceXComponent.tsx
351
- import {useStore} from "reca";
352
- import {SpaceXStore} from "../stores/SpaceXStore.js";
353
-
354
- export const TestStoreComponent = (): JSX.Element => {
355
- const store = useStore(SpaceXStore);
356
-
357
- return (
358
- <div>
359
- <p>
360
- Company:
361
- {" "}
362
-
363
- {store.companyInfo.name}
364
- </p>
365
-
366
- <p>
367
- Founder:
368
- {" "}
369
-
370
- {store.companyInfo.founder}
371
- </p>
372
- </div>
373
- );
374
- };
375
-
376
- ```
377
-
378
- ## Documentation and Resources
379
-
380
- ### Documentation
381
-
382
- - **[Wiki](https://github.com/LabEG/reca/wiki)** - Comprehensive guides, tutorials, and API reference
383
- - **[API Documentation](https://github.com/LabEG/reca/wiki)** - Detailed API documentation for all features
384
-
385
- ### Community and Support
386
-
387
- - **[Discord Server](https://discordapp.com/channels/974049080454045796/974049142022209566)** - Join our community for real-time help and discussions
388
- - **[GitHub Discussions](https://github.com/LabEG/reca/discussions)** - Ask questions and share ideas
389
- - **[GitHub Issues](https://github.com/LabEG/reca/issues)** - Report bugs or request features
390
-
391
- ### Contributing
392
-
393
- We welcome contributions! See our:
394
-
395
- - **[Contributing Guide](CONTRIBUTING.md)** - Learn how to contribute to the project
396
- - **[Code of Conduct](CODE_OF_CONDUCT.md)** - Our community guidelines
397
- - **[Security Policy](SECURITY.md)** - How to report security vulnerabilities
398
-
399
- ## License
400
-
401
- ReCA is [MIT licensed](https://github.com/LabEG/reca/blob/main/LICENSE).
5
+ For full documentation, please see the [main README](../../README.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reca",
3
- "version": "2.4.8",
3
+ "version": "2.4.11",
4
4
  "description": "ReCA - React Clean Architecture state manager",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -8,10 +8,9 @@
8
8
  "build": "npm run clean && tsc --project tsconfig.build.json && node ./dist/index.js",
9
9
  "clean": "rimraf ./dist",
10
10
  "lint": "eslint --fix ./src/",
11
- "test": "node --import ./ts-loader.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.tsx\"",
12
- "test-watch": "node --watch --import ./ts-loader.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.tsx\"",
13
- "coverage": "node --import ./ts-loader.js --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info \"tests/**/*.spec.tsx\"",
14
- "release": "standard-version",
11
+ "test": "node --import ./register-ts-node.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.tsx\"",
12
+ "test-watch": "node --watch --import ./register-ts-node.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.tsx\"",
13
+ "coverage": "node --import ./register-ts-node.js --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info \"tests/**/*.spec.tsx\"",
15
14
  "prepublishOnly": "npm run test && npm run build"
16
15
  },
17
16
  "keywords": [
@@ -34,25 +33,24 @@
34
33
  "LICENSE"
35
34
  ],
36
35
  "dependencies": {
37
- "first-di": "^3.4.11"
36
+ "first-di": "^3.4.12"
38
37
  },
39
38
  "peerDependencies": {
40
39
  "react": ">=16.0.0",
41
40
  "reflect-metadata": ">=0.1.0"
42
41
  },
43
42
  "devDependencies": {
44
- "@testing-library/react": "^16.3.1",
45
- "@swc-node/register": "^1.11.1",
43
+ "@testing-library/react": "^16.3.2",
44
+ "@swc/core": "^1.15.11",
46
45
  "@types/chai": "^5.2.3",
47
46
  "@types/react": "^19.2.7",
48
47
  "chai": "^6.2.2",
49
- "global-jsdom": "^27.0.0",
50
- "jsdom": "^27.4.0",
48
+ "global-jsdom": "^28.0.0",
49
+ "jsdom": "^28.0.0",
51
50
  "react": "^19.2.3",
52
51
  "react-dom": "^19.2.3",
53
52
  "reflect-metadata": "^0.2.2",
54
53
  "rimraf": "^6.1.2",
55
- "standard-version": "^9.5.0",
56
54
  "ts-node": "^10.9.2",
57
55
  "typescript": "^5.9.3"
58
56
  }