@blac/core 2.0.0 โ 2.0.1
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 +182 -171
- package/dist/index.cjs +2993 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1895 -0
- package/dist/index.d.ts +1752 -362
- package/dist/index.js +2884 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -19
- package/dist/index.cjs.js +0 -2
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -670
- package/dist/index.es.js.map +0 -1
- package/src/Blac.ts +0 -483
- package/src/BlacEvent.ts +0 -5
- package/src/BlacObservable.ts +0 -88
- package/src/BlacObserver.ts +0 -129
- package/src/BlacPlugin.ts +0 -13
- package/src/Bloc.ts +0 -109
- package/src/BlocBase.ts +0 -248
- package/src/Cubit.ts +0 -69
- package/src/addons/BlacAddon.ts +0 -17
- package/src/addons/Persist.ts +0 -105
- package/src/addons/index.ts +0 -1
- package/src/index.ts +0 -8
- package/src/types.ts +0 -63
package/README.md
CHANGED
|
@@ -1,252 +1,263 @@
|
|
|
1
1
|
# @blac/core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- ๐ Predictable unidirectional data flow
|
|
8
|
-
- ๐งฉ Modular architecture with Blocs and Cubits
|
|
9
|
-
- ๐งช Unit test friendly
|
|
10
|
-
- ๐ Isolated state instances when needed
|
|
11
|
-
- ๐ Plugin system for extensibility
|
|
3
|
+
Core state management library implementing the BloC pattern for TypeScript applications.
|
|
12
4
|
|
|
13
5
|
## Installation
|
|
14
6
|
|
|
15
|
-
Install `@blac/core` using your favorite package manager:
|
|
16
|
-
|
|
17
7
|
```bash
|
|
18
|
-
|
|
8
|
+
npm install @blac/core
|
|
9
|
+
# or
|
|
19
10
|
pnpm add @blac/core
|
|
20
|
-
|
|
21
|
-
# yarn
|
|
11
|
+
# or
|
|
22
12
|
yarn add @blac/core
|
|
23
|
-
|
|
24
|
-
# npm
|
|
25
|
-
npm install @blac/core
|
|
26
13
|
```
|
|
27
14
|
|
|
28
15
|
## Core Concepts
|
|
29
16
|
|
|
30
|
-
###
|
|
17
|
+
### Cubit
|
|
31
18
|
|
|
32
|
-
|
|
19
|
+
Simple state container with direct state emission. Use when you need straightforward state updates.
|
|
33
20
|
|
|
34
21
|
```typescript
|
|
35
|
-
|
|
22
|
+
import { Cubit } from '@blac/core';
|
|
23
|
+
|
|
24
|
+
class CounterCubit extends Cubit<{ count: number }> {
|
|
36
25
|
constructor() {
|
|
37
|
-
super(0);
|
|
26
|
+
super({ count: 0 });
|
|
38
27
|
}
|
|
39
28
|
|
|
40
|
-
increment
|
|
41
|
-
this.emit(this.state + 1);
|
|
29
|
+
increment() {
|
|
30
|
+
this.emit({ count: this.state.count + 1 });
|
|
42
31
|
}
|
|
43
32
|
|
|
44
|
-
decrement
|
|
45
|
-
this.
|
|
33
|
+
decrement() {
|
|
34
|
+
this.update((state) => ({ count: state.count - 1 }));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
reset() {
|
|
38
|
+
this.patch({ count: 0 });
|
|
46
39
|
}
|
|
47
40
|
}
|
|
48
41
|
```
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
### Vertex
|
|
44
|
+
|
|
45
|
+
Event-driven state container using discriminated union events. Use when you need structured event handling with type-safe exhaustive checking.
|
|
51
46
|
|
|
52
47
|
```typescript
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
| { type: '
|
|
48
|
+
import { Vertex } from '@blac/core';
|
|
49
|
+
|
|
50
|
+
type CounterEvent =
|
|
51
|
+
| { type: 'increment'; amount: number }
|
|
52
|
+
| { type: 'decrement' }
|
|
53
|
+
| { type: 'reset' };
|
|
57
54
|
|
|
58
|
-
class
|
|
55
|
+
class CounterVertex extends Vertex<{ count: number }, CounterEvent> {
|
|
59
56
|
constructor() {
|
|
60
|
-
super(0);
|
|
57
|
+
super({ count: 0 });
|
|
58
|
+
this.createHandlers({
|
|
59
|
+
increment: (event, emit) => {
|
|
60
|
+
emit({ count: this.state.count + event.amount });
|
|
61
|
+
},
|
|
62
|
+
decrement: (_, emit) => {
|
|
63
|
+
emit({ count: this.state.count - 1 });
|
|
64
|
+
},
|
|
65
|
+
reset: (_, emit) => {
|
|
66
|
+
emit({ count: 0 });
|
|
67
|
+
},
|
|
68
|
+
});
|
|
61
69
|
}
|
|
62
70
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return state - action.amount;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
+
increment = (amount = 1) => this.add({ type: 'increment', amount });
|
|
72
|
+
decrement = () => this.add({ type: 'decrement' });
|
|
73
|
+
reset = () => this.add({ type: 'reset' });
|
|
74
|
+
}
|
|
75
|
+
```
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
### StatelessCubit & StatelessVertex
|
|
78
|
+
|
|
79
|
+
Stateless containers for services that don't need state tracking. Use for actions-only functionality like analytics, API calls, or navigation.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { StatelessCubit } from '@blac/core';
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
class AnalyticsService extends StatelessCubit {
|
|
85
|
+
trackEvent(name: string, data?: Record<string, unknown>) {
|
|
86
|
+
// Send to analytics provider
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
89
|
```
|
|
81
90
|
|
|
82
|
-
|
|
91
|
+
## Registry API
|
|
83
92
|
|
|
84
|
-
|
|
93
|
+
Manage state container instances with the registry functions:
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
import { acquire, release, borrow, hasInstance, clear } from '@blac/core';
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
// Acquire an instance (creates if needed, increments ref count)
|
|
99
|
+
const counter = acquire(CounterCubit);
|
|
89
100
|
|
|
90
|
-
|
|
101
|
+
// Release when done (decrements ref count, disposes when 0)
|
|
102
|
+
release(CounterCubit);
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
increment = () => {
|
|
99
|
-
this.emit(this.state + 1);
|
|
100
|
-
}
|
|
104
|
+
// Borrow without affecting ref count
|
|
105
|
+
const instance = borrow(CounterCubit);
|
|
106
|
+
|
|
107
|
+
// Check if instance exists
|
|
108
|
+
if (hasInstance(CounterCubit)) {
|
|
109
|
+
// ...
|
|
101
110
|
}
|
|
111
|
+
|
|
112
|
+
// Clear a specific class
|
|
113
|
+
clear(CounterCubit);
|
|
114
|
+
|
|
115
|
+
// Clear all instances
|
|
116
|
+
clearAll();
|
|
102
117
|
```
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
## Decorators
|
|
105
120
|
|
|
106
|
-
|
|
121
|
+
Use the `@blac` decorator to configure container behavior:
|
|
107
122
|
|
|
108
123
|
```typescript
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
124
|
+
import { Cubit, blac } from '@blac/core';
|
|
125
|
+
|
|
126
|
+
@blac({ isolated: true }) // Each consumer gets its own instance
|
|
127
|
+
class FormCubit extends Cubit<FormState> {}
|
|
128
|
+
|
|
129
|
+
@blac({ keepAlive: true }) // Never auto-dispose
|
|
130
|
+
class AuthCubit extends Cubit<AuthState> {}
|
|
131
|
+
|
|
132
|
+
@blac({ excludeFromDevTools: true }) // Hide from DevTools
|
|
133
|
+
class InternalCubit extends Cubit<State> {}
|
|
120
134
|
```
|
|
121
135
|
|
|
122
|
-
|
|
136
|
+
## Utilities
|
|
137
|
+
|
|
138
|
+
### waitUntil
|
|
123
139
|
|
|
124
|
-
|
|
140
|
+
Wait for a specific state condition:
|
|
125
141
|
|
|
126
142
|
```typescript
|
|
127
|
-
|
|
128
|
-
static keepAlive = true; // State persists even when no consumers
|
|
129
|
-
|
|
130
|
-
constructor() {
|
|
131
|
-
super(0);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
increment = () => {
|
|
135
|
-
this.emit(this.state + 1);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
```
|
|
143
|
+
import { waitUntil } from '@blac/core';
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
const counter = acquire(CounterCubit);
|
|
146
|
+
|
|
147
|
+
// Wait until count reaches 10
|
|
148
|
+
await waitUntil(counter, (state) => state.count >= 10);
|
|
149
|
+
|
|
150
|
+
// With timeout
|
|
151
|
+
await waitUntil(counter, (state) => state.count >= 10, {
|
|
152
|
+
timeout: 5000,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
141
155
|
|
|
142
|
-
###
|
|
156
|
+
### watch
|
|
143
157
|
|
|
144
|
-
Create
|
|
158
|
+
Create computed values that react to state changes:
|
|
145
159
|
|
|
146
160
|
```typescript
|
|
147
|
-
import {
|
|
148
|
-
|
|
149
|
-
class
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
import { watch, instance } from '@blac/core';
|
|
162
|
+
|
|
163
|
+
class DashboardCubit extends Cubit<DashboardState> {
|
|
164
|
+
constructor() {
|
|
165
|
+
super({ items: [] });
|
|
166
|
+
|
|
167
|
+
// Watch another bloc's state
|
|
168
|
+
watch(
|
|
169
|
+
instance(UserCubit),
|
|
170
|
+
(userState) => userState.preferences,
|
|
171
|
+
(preferences) => this.onPreferencesChanged(preferences),
|
|
172
|
+
);
|
|
156
173
|
}
|
|
157
174
|
}
|
|
158
|
-
|
|
159
|
-
// Add the plugin to Blac
|
|
160
|
-
import { Blac } from '@blac/core';
|
|
161
|
-
Blac.addPlugin(new LoggerPlugin());
|
|
162
175
|
```
|
|
163
176
|
|
|
164
|
-
|
|
177
|
+
## Plugins
|
|
165
178
|
|
|
166
|
-
|
|
179
|
+
Extend functionality with plugins:
|
|
167
180
|
|
|
168
181
|
```typescript
|
|
169
|
-
import {
|
|
182
|
+
import { getPluginManager, type BlacPlugin } from '@blac/core';
|
|
170
183
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
184
|
+
const loggingPlugin: BlacPlugin = {
|
|
185
|
+
name: 'logging',
|
|
186
|
+
onStateChange: (container, prevState, newState) => {
|
|
187
|
+
console.log(`[${container.constructor.name}]`, prevState, '->', newState);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
175
190
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
loading: boolean;
|
|
179
|
-
userData: { id: string; name: string; bio?: string } | null;
|
|
180
|
-
error: string | null;
|
|
181
|
-
}
|
|
191
|
+
getPluginManager().register(loggingPlugin);
|
|
192
|
+
```
|
|
182
193
|
|
|
183
|
-
|
|
184
|
-
type UserProfileAction = { type: 'dataLoaded', data: any } | { type: 'error', error: string };
|
|
185
|
-
|
|
186
|
-
class UserProfileBloc extends Bloc<UserProfileState, UserProfileAction> {
|
|
187
|
-
private userId: string;
|
|
188
|
-
|
|
189
|
-
// The Blac library or its React bindings (like @blac/react)
|
|
190
|
-
// might provide a way to pass these props during instantiation.
|
|
191
|
-
// For example, `useBloc(UserProfileBloc, { props: { userId: '123' } })`
|
|
192
|
-
constructor(props: UserProfileProps) {
|
|
193
|
-
super({ loading: true, userData: null, error: null }); // Initial state
|
|
194
|
-
this.userId = props.userId;
|
|
195
|
-
// Optional: Set a dynamic name for easier debugging with multiple instances
|
|
196
|
-
this._name = `UserProfileBloc_${this.userId}`;
|
|
197
|
-
this.fetchUserProfile();
|
|
198
|
-
}
|
|
194
|
+
## Configuration
|
|
199
195
|
|
|
200
|
-
|
|
201
|
-
reducer = (action: UserProfileAction, state: UserProfileState): UserProfileState => {
|
|
202
|
-
switch (action.type) {
|
|
203
|
-
case 'dataLoaded':
|
|
204
|
-
return { ...state, loading: false, userData: action.data, error: null };
|
|
205
|
-
case 'error':
|
|
206
|
-
return { ...state, loading: false, error: action.error };
|
|
207
|
-
default:
|
|
208
|
-
return state;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
196
|
+
Configure global behavior:
|
|
211
197
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// Dispatch an action or directly emit a new state
|
|
219
|
-
this.add({ type: 'dataLoaded', data: mockUserData });
|
|
220
|
-
} catch (e:any) {
|
|
221
|
-
this.add({ type: 'error', error: e.message || 'Failed to fetch user profile' });
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
198
|
+
```typescript
|
|
199
|
+
import { configureBlac } from '@blac/core';
|
|
200
|
+
|
|
201
|
+
configureBlac({
|
|
202
|
+
devMode: import.meta.env.DEV,
|
|
203
|
+
});
|
|
225
204
|
```
|
|
226
205
|
|
|
227
206
|
## API Reference
|
|
228
207
|
|
|
229
|
-
###
|
|
208
|
+
### State Containers
|
|
230
209
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
210
|
+
| Class | Description |
|
|
211
|
+
| -------------------- | ----------------------------------------------------------- |
|
|
212
|
+
| `Cubit<S, P>` | Simple state container with `emit()`, `update()`, `patch()` |
|
|
213
|
+
| `Vertex<S, E, P>` | Event-driven container with `add()` and `createHandlers()` |
|
|
214
|
+
| `StatelessCubit` | Stateless service container |
|
|
215
|
+
| `StatelessVertex<E>` | Stateless event-driven container |
|
|
235
216
|
|
|
236
|
-
###
|
|
217
|
+
### Registry Functions
|
|
237
218
|
|
|
238
|
-
|
|
219
|
+
| Function | Description |
|
|
220
|
+
| -------------------------------- | ------------------------------------------- |
|
|
221
|
+
| `acquire(Class, key?, options?)` | Get or create instance, increment ref count |
|
|
222
|
+
| `release(Class, key?)` | Decrement ref count, dispose when 0 |
|
|
223
|
+
| `borrow(Class, key?)` | Get instance without affecting ref count |
|
|
224
|
+
| `borrowSafe(Class, key?)` | Borrow or return undefined |
|
|
225
|
+
| `ensure(Class, key?, options?)` | Acquire without incrementing ref count |
|
|
226
|
+
| `hasInstance(Class, key?)` | Check if instance exists |
|
|
227
|
+
| `getRefCount(Class, key?)` | Get current reference count |
|
|
228
|
+
| `clear(Class)` | Remove all instances of a class |
|
|
229
|
+
| `clearAll()` | Remove all instances |
|
|
239
230
|
|
|
240
|
-
###
|
|
231
|
+
### Exports
|
|
241
232
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
```typescript
|
|
234
|
+
// Core classes
|
|
235
|
+
export { Cubit, Vertex, StatelessCubit, StatelessVertex } from '@blac/core';
|
|
236
|
+
|
|
237
|
+
// Registry
|
|
238
|
+
export {
|
|
239
|
+
acquire,
|
|
240
|
+
release,
|
|
241
|
+
borrow,
|
|
242
|
+
ensure,
|
|
243
|
+
hasInstance,
|
|
244
|
+
clear,
|
|
245
|
+
clearAll,
|
|
246
|
+
} from '@blac/core';
|
|
247
|
+
|
|
248
|
+
// Utilities
|
|
249
|
+
export { waitUntil, watch, instance } from '@blac/core';
|
|
250
|
+
|
|
251
|
+
// Decorators
|
|
252
|
+
export { blac } from '@blac/core';
|
|
253
|
+
|
|
254
|
+
// Plugin system
|
|
255
|
+
export { getPluginManager, type BlacPlugin } from '@blac/core';
|
|
256
|
+
|
|
257
|
+
// Configuration
|
|
258
|
+
export { configureBlac, getBlacConfig, isDevMode } from '@blac/core';
|
|
259
|
+
```
|
|
249
260
|
|
|
250
261
|
## License
|
|
251
262
|
|
|
252
|
-
|
|
263
|
+
MIT
|