@blac/core 2.0.0-rn-12 → 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 +178 -244
- 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 +1715 -644
- package/dist/index.js +2884 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -18
- package/dist/index.cjs.js +0 -11
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -1588
- package/dist/index.es.js.map +0 -1
- package/src/Blac.ts +0 -789
- package/src/BlacObserver.ts +0 -186
- package/src/Bloc.ts +0 -138
- package/src/BlocBase.ts +0 -589
- package/src/Cubit.ts +0 -83
- package/src/__tests__/Blac.config.test.ts +0 -86
- package/src/__tests__/Bloc.event.test.ts +0 -419
- package/src/__tests__/BlocBase.lifecycle.test.ts +0 -441
- package/src/__tests__/Cubit.test.ts +0 -473
- package/src/__tests__/props.test.ts +0 -256
- package/src/adapter/BlacAdapter.ts +0 -327
- package/src/adapter/ConsumerTracker.ts +0 -241
- package/src/adapter/ProxyFactory.ts +0 -308
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -561
- package/src/adapter/__tests__/ConsumerTracker.test.ts +0 -562
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/adapter/index.ts +0 -3
- package/src/events.ts +0 -3
- package/src/index.ts +0 -17
- package/src/testing.ts +0 -252
- package/src/types.ts +0 -91
- package/src/utils/shallowEqual.ts +0 -40
- package/src/utils/uuid.ts +0 -22
package/src/BlacObserver.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { Blac } from './Blac';
|
|
2
|
-
import { BlocLifecycleState } from './BlocBase';
|
|
3
|
-
import { BlocBase } from './BlocBase';
|
|
4
|
-
import { BlocHookDependencyArrayFn } from './types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Represents an observer that can subscribe to state changes in a Bloc
|
|
8
|
-
* @template S - The type of state being observed
|
|
9
|
-
*/
|
|
10
|
-
export type BlacObserver<S> = {
|
|
11
|
-
/** Function to be called when state changes */
|
|
12
|
-
fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>;
|
|
13
|
-
/** Optional function to determine if the observer should be notified of state changes */
|
|
14
|
-
dependencyArray?: BlocHookDependencyArrayFn<S>;
|
|
15
|
-
/** Dispose function for the observer */
|
|
16
|
-
dispose?: () => void;
|
|
17
|
-
/** Cached state values used for dependency comparison */
|
|
18
|
-
lastState?: unknown[];
|
|
19
|
-
/** Unique identifier for the observer */
|
|
20
|
-
id: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A class that manages observers for a Bloc's state changes
|
|
25
|
-
* @template S - The type of state being observed
|
|
26
|
-
*/
|
|
27
|
-
export class BlacObservable<S = unknown> {
|
|
28
|
-
/** The Bloc instance this observable is associated with */
|
|
29
|
-
bloc: BlocBase<S>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Creates a new BlacObservable instance
|
|
33
|
-
* @param bloc - The Bloc instance to observe
|
|
34
|
-
*/
|
|
35
|
-
constructor(bloc: BlocBase<S>) {
|
|
36
|
-
this.bloc = bloc;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private _observers = new Set<BlacObserver<S>>();
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Gets the number of active observers
|
|
43
|
-
* @returns The number of observers currently subscribed
|
|
44
|
-
*/
|
|
45
|
-
get size(): number {
|
|
46
|
-
return this._observers.size;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Gets the set of all observers
|
|
51
|
-
* @returns The Set of all BlacObserver instances
|
|
52
|
-
*/
|
|
53
|
-
get observers() {
|
|
54
|
-
return this._observers;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Subscribes an observer to state changes
|
|
59
|
-
* @param observer - The observer to subscribe
|
|
60
|
-
* @returns A function that can be called to unsubscribe the observer
|
|
61
|
-
*/
|
|
62
|
-
subscribe(observer: BlacObserver<S>): () => void {
|
|
63
|
-
// Check if bloc is disposed or in disposal process
|
|
64
|
-
const disposalState = (this.bloc as any)._disposalState;
|
|
65
|
-
if (
|
|
66
|
-
disposalState === BlocLifecycleState.DISPOSED ||
|
|
67
|
-
disposalState === BlocLifecycleState.DISPOSING
|
|
68
|
-
) {
|
|
69
|
-
Blac.log(
|
|
70
|
-
'BlacObservable.subscribe: Cannot subscribe to disposed/disposing bloc.',
|
|
71
|
-
this.bloc,
|
|
72
|
-
observer,
|
|
73
|
-
);
|
|
74
|
-
return () => {}; // Return no-op unsubscribe
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
Blac.log(
|
|
78
|
-
'BlacObservable.subscribe: Subscribing observer.',
|
|
79
|
-
this.bloc,
|
|
80
|
-
observer,
|
|
81
|
-
);
|
|
82
|
-
this._observers.add(observer);
|
|
83
|
-
|
|
84
|
-
// If we're in DISPOSAL_REQUESTED state, cancel the disposal since we have a new observer
|
|
85
|
-
if (disposalState === BlocLifecycleState.DISPOSAL_REQUESTED) {
|
|
86
|
-
Blac.log(
|
|
87
|
-
'BlacObservable.subscribe: Cancelling disposal due to new subscription.',
|
|
88
|
-
this.bloc,
|
|
89
|
-
);
|
|
90
|
-
// Transition back to active state
|
|
91
|
-
(this.bloc as any)._atomicStateTransition(
|
|
92
|
-
BlocLifecycleState.DISPOSAL_REQUESTED,
|
|
93
|
-
BlocLifecycleState.ACTIVE,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Don't initialize lastState here - let it remain undefined for first-time detection
|
|
98
|
-
return () => {
|
|
99
|
-
Blac.log(
|
|
100
|
-
'BlacObservable.subscribe: Unsubscribing observer.',
|
|
101
|
-
this.bloc,
|
|
102
|
-
observer,
|
|
103
|
-
);
|
|
104
|
-
this.unsubscribe(observer);
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Unsubscribes an observer from state changes
|
|
110
|
-
* @param observer - The observer to unsubscribe
|
|
111
|
-
*/
|
|
112
|
-
unsubscribe(observer: BlacObserver<S>) {
|
|
113
|
-
this._observers.delete(observer);
|
|
114
|
-
// Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_REMOVED, this.bloc, { listenerId: observer.id });
|
|
115
|
-
|
|
116
|
-
if (this.size === 0) {
|
|
117
|
-
Blac.log('BlacObservable.unsubscribe: No observers left.', this.bloc);
|
|
118
|
-
// Check if bloc should be disposed when both observers and consumers are gone
|
|
119
|
-
if (
|
|
120
|
-
this.bloc._consumers.size === 0 &&
|
|
121
|
-
!this.bloc._keepAlive &&
|
|
122
|
-
(this.bloc as any)._disposalState === BlocLifecycleState.ACTIVE
|
|
123
|
-
) {
|
|
124
|
-
Blac.log(
|
|
125
|
-
`[${this.bloc._name}:${this.bloc._id}] No observers or consumers left. Scheduling disposal.`,
|
|
126
|
-
);
|
|
127
|
-
(this.bloc as any)._scheduleDisposal();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Notifies all observers of a state change
|
|
134
|
-
* @param newState - The new state value
|
|
135
|
-
* @param oldState - The previous state value
|
|
136
|
-
* @param action - Optional action that triggered the state change
|
|
137
|
-
*/
|
|
138
|
-
notify(newState: S, oldState: S, action?: unknown) {
|
|
139
|
-
this._observers.forEach((observer) => {
|
|
140
|
-
let shouldUpdate = false;
|
|
141
|
-
|
|
142
|
-
if (observer.dependencyArray) {
|
|
143
|
-
const lastDependencyCheck = observer.lastState;
|
|
144
|
-
const newDependencyCheck = observer.dependencyArray(
|
|
145
|
-
newState,
|
|
146
|
-
oldState,
|
|
147
|
-
this.bloc,
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// If this is the first time (no lastState), trigger initial render
|
|
151
|
-
if (!lastDependencyCheck) {
|
|
152
|
-
shouldUpdate = true;
|
|
153
|
-
} else {
|
|
154
|
-
// Compare dependency arrays
|
|
155
|
-
if (lastDependencyCheck.length !== newDependencyCheck.length) {
|
|
156
|
-
shouldUpdate = true;
|
|
157
|
-
} else {
|
|
158
|
-
// Compare each dependency value using Object.is (same as React)
|
|
159
|
-
for (let i = 0; i < newDependencyCheck.length; i++) {
|
|
160
|
-
if (!Object.is(lastDependencyCheck[i], newDependencyCheck[i])) {
|
|
161
|
-
shouldUpdate = true;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
observer.lastState = newDependencyCheck;
|
|
169
|
-
} else {
|
|
170
|
-
shouldUpdate = true;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (shouldUpdate) {
|
|
174
|
-
void observer.fn(newState, oldState, action);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Clears the observer set
|
|
181
|
-
*/
|
|
182
|
-
clear() {
|
|
183
|
-
// Just clear the observers without calling unsubscribe to avoid circular disposal
|
|
184
|
-
this._observers.clear();
|
|
185
|
-
}
|
|
186
|
-
}
|
package/src/Bloc.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { Blac } from './Blac';
|
|
2
|
-
import { BlocBase } from './BlocBase';
|
|
3
|
-
import { BlocEventConstraint } from './types';
|
|
4
|
-
|
|
5
|
-
export abstract class Bloc<
|
|
6
|
-
S, // State type
|
|
7
|
-
A extends BlocEventConstraint = BlocEventConstraint, // Base Action/Event type with proper constraints
|
|
8
|
-
P = unknown, // Props type
|
|
9
|
-
> extends BlocBase<S, P> {
|
|
10
|
-
readonly eventHandlers: Map<
|
|
11
|
-
new (...args: any[]) => A,
|
|
12
|
-
(event: A, emit: (newState: S) => void) => void | Promise<void>
|
|
13
|
-
> = new Map();
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @internal
|
|
17
|
-
* Event queue to ensure sequential processing of async events
|
|
18
|
-
*/
|
|
19
|
-
private _eventQueue: A[] = [];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @internal
|
|
23
|
-
* Flag indicating if an event is currently being processed
|
|
24
|
-
*/
|
|
25
|
-
private _isProcessingEvent = false;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Registers an event handler for a specific event type.
|
|
29
|
-
* This method is typically called in the constructor of a derived Bloc class.
|
|
30
|
-
* @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent).
|
|
31
|
-
* @param handler A function that processes the event and can emit new states.
|
|
32
|
-
* The 'event' parameter in the handler will be typed to the specific eventConstructor.
|
|
33
|
-
*/
|
|
34
|
-
protected on<E extends A>(
|
|
35
|
-
eventConstructor: new (...args: any[]) => E,
|
|
36
|
-
handler: (event: E, emit: (newState: S) => void) => void | Promise<void>,
|
|
37
|
-
): void {
|
|
38
|
-
if (this.eventHandlers.has(eventConstructor)) {
|
|
39
|
-
Blac.warn(
|
|
40
|
-
`[Bloc ${this._name}:${String(this._id)}] Handler for event '${eventConstructor.name}' already registered. It will be overwritten.`,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
this.eventHandlers.set(
|
|
44
|
-
eventConstructor,
|
|
45
|
-
handler as (
|
|
46
|
-
event: A,
|
|
47
|
-
emit: (newState: S) => void,
|
|
48
|
-
) => void | Promise<void>,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Dispatches an action/event to the Bloc.
|
|
54
|
-
* Events are queued and processed sequentially to prevent race conditions.
|
|
55
|
-
* @param action The action/event instance to be processed.
|
|
56
|
-
*/
|
|
57
|
-
public add = async (action: A): Promise<void> => {
|
|
58
|
-
this._eventQueue.push(action);
|
|
59
|
-
|
|
60
|
-
if (!this._isProcessingEvent) {
|
|
61
|
-
await this._processEventQueue();
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @internal
|
|
67
|
-
* Processes events from the queue sequentially
|
|
68
|
-
*/
|
|
69
|
-
private async _processEventQueue(): Promise<void> {
|
|
70
|
-
if (this._isProcessingEvent) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this._isProcessingEvent = true;
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
while (this._eventQueue.length > 0) {
|
|
78
|
-
const action = this._eventQueue.shift()!;
|
|
79
|
-
await this._processEvent(action);
|
|
80
|
-
}
|
|
81
|
-
} finally {
|
|
82
|
-
this._isProcessingEvent = false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @internal
|
|
88
|
-
* Processes a single event
|
|
89
|
-
*/
|
|
90
|
-
private async _processEvent(action: A): Promise<void> {
|
|
91
|
-
const eventConstructor = action.constructor as new (...args: any[]) => A;
|
|
92
|
-
const handler = this.eventHandlers.get(eventConstructor);
|
|
93
|
-
|
|
94
|
-
if (handler) {
|
|
95
|
-
const emit = (newState: S): void => {
|
|
96
|
-
const previousState = this.state; // State just before this specific emission
|
|
97
|
-
this._pushState(newState, previousState, action);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
await handler(action, emit);
|
|
102
|
-
} catch (error) {
|
|
103
|
-
const constructorName =
|
|
104
|
-
(action.constructor as { name?: string }).name ||
|
|
105
|
-
'UnnamedConstructor';
|
|
106
|
-
const errorContext = {
|
|
107
|
-
blocName: this._name,
|
|
108
|
-
blocId: String(this._id),
|
|
109
|
-
eventType: constructorName,
|
|
110
|
-
currentState: this.state,
|
|
111
|
-
action: action,
|
|
112
|
-
timestamp: new Date().toISOString(),
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
Blac.error(
|
|
116
|
-
`[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${constructorName}':`,
|
|
117
|
-
error,
|
|
118
|
-
'Context:',
|
|
119
|
-
errorContext,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
if (error instanceof Error && error.name === 'CriticalError') {
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
const constructorName =
|
|
128
|
-
(action.constructor as { name?: string }).name || 'UnnamedConstructor';
|
|
129
|
-
Blac.warn(
|
|
130
|
-
`[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${constructorName}'.`,
|
|
131
|
-
'Registered handlers:',
|
|
132
|
-
Array.from(this.eventHandlers.keys()).map((k) => k.name),
|
|
133
|
-
'Action was:',
|
|
134
|
-
action,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|