@blac/core 2.0.0-rc-16 → 2.0.0-rc.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/dist/.tsbuildinfo +1 -0
- package/dist/Blac.d.ts +246 -0
- package/dist/Blac.d.ts.map +1 -0
- package/dist/Blac.js +624 -0
- package/dist/Blac.js.map +1 -0
- package/dist/Bloc.d.ts +41 -0
- package/dist/Bloc.d.ts.map +1 -0
- package/dist/Bloc.js +140 -0
- package/dist/Bloc.js.map +1 -0
- package/dist/BlocBase.d.ts +110 -0
- package/dist/BlocBase.d.ts.map +1 -0
- package/dist/BlocBase.js +287 -0
- package/dist/BlocBase.js.map +1 -0
- package/dist/Cubit.d.ts +25 -0
- package/dist/Cubit.d.ts.map +1 -0
- package/dist/Cubit.js +64 -0
- package/dist/Cubit.js.map +1 -0
- package/dist/adapter/BlacAdapter.d.ts +67 -0
- package/dist/adapter/BlacAdapter.d.ts.map +1 -0
- package/dist/adapter/BlacAdapter.js +263 -0
- package/dist/adapter/BlacAdapter.js.map +1 -0
- package/dist/adapter/ProxyFactory.d.ts +50 -0
- package/dist/adapter/ProxyFactory.d.ts.map +1 -0
- package/dist/adapter/ProxyFactory.js +150 -0
- package/dist/adapter/ProxyFactory.js.map +1 -0
- package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +19 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/errors/BlacError.d.ts +38 -0
- package/dist/errors/BlacError.d.ts.map +1 -0
- package/dist/errors/BlacError.js +55 -0
- package/dist/errors/BlacError.js.map +1 -0
- package/dist/errors/ErrorManager.d.ts +39 -0
- package/dist/errors/ErrorManager.d.ts.map +1 -0
- package/dist/errors/ErrorManager.js +91 -0
- package/dist/errors/ErrorManager.js.map +1 -0
- package/dist/errors/handleError.d.ts +24 -0
- package/dist/errors/handleError.d.ts.map +1 -0
- package/dist/errors/handleError.js +62 -0
- package/dist/errors/handleError.js.map +1 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/{src/events.ts → dist/events.d.ts} +4 -2
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/index.cjs +38 -0
- package/{src/index.ts → dist/index.d.ts} +1 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
- package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
- package/dist/lifecycle/BlocLifecycle.js +123 -0
- package/dist/lifecycle/BlocLifecycle.js.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +18 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
- package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/BlocPluginRegistry.js +201 -0
- package/dist/plugins/BlocPluginRegistry.js.map +1 -0
- package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
- package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/SystemPluginRegistry.js +184 -0
- package/dist/plugins/SystemPluginRegistry.js.map +1 -0
- package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +26 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +103 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +3 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/subscription/SubscriptionManager.d.ts +67 -0
- package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscription/SubscriptionManager.js +317 -0
- package/dist/subscription/SubscriptionManager.js.map +1 -0
- package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
- package/dist/subscription/index.d.ts.map +1 -0
- package/dist/subscription/index.js +19 -0
- package/dist/subscription/index.js.map +1 -0
- package/dist/subscription/types.d.ts +65 -0
- package/dist/subscription/types.d.ts.map +1 -0
- package/dist/subscription/types.js +3 -0
- package/dist/subscription/types.js.map +1 -0
- package/dist/testing.d.ts +33 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +68 -0
- package/dist/testing.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +11 -28
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/BatchingManager.d.ts +28 -0
- package/dist/utils/BatchingManager.d.ts.map +1 -0
- package/dist/utils/BatchingManager.js +48 -0
- package/dist/utils/BatchingManager.js.map +1 -0
- package/dist/utils/RerenderLogger.d.ts +60 -0
- package/dist/utils/RerenderLogger.d.ts.map +1 -0
- package/dist/utils/RerenderLogger.js +205 -0
- package/dist/utils/RerenderLogger.js.map +1 -0
- package/dist/utils/generateInstanceId.d.ts +11 -0
- package/dist/utils/generateInstanceId.d.ts.map +1 -0
- package/dist/utils/generateInstanceId.js +50 -0
- package/dist/utils/generateInstanceId.js.map +1 -0
- package/dist/utils/shallowEqual.d.ts +9 -0
- package/dist/utils/shallowEqual.d.ts.map +1 -0
- package/dist/utils/shallowEqual.js +35 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/utils/uuid.d.ts +10 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +24 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +19 -6
- package/src/Blac.ts +0 -802
- package/src/Bloc.ts +0 -180
- package/src/BlocBase.ts +0 -398
- package/src/Cubit.ts +0 -68
- package/src/__tests__/Blac.config.test.ts +0 -79
- package/src/__tests__/Bloc.event.test.ts +0 -394
- package/src/__tests__/BlocBase.subscription.test.ts +0 -349
- package/src/__tests__/Cubit.test.ts +0 -462
- package/src/__tests__/memory-leaks.test.ts +0 -383
- package/src/__tests__/plugins.test.ts +0 -464
- package/src/adapter/BlacAdapter.ts +0 -467
- package/src/adapter/ProxyFactory.ts +0 -249
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/errors/BlacError.ts +0 -69
- package/src/errors/ErrorManager.ts +0 -101
- package/src/errors/handleError.ts +0 -114
- package/src/errors/index.ts +0 -13
- package/src/lifecycle/BlocLifecycle.ts +0 -156
- package/src/lifecycle/index.ts +0 -1
- package/src/plugins/BlocPluginRegistry.ts +0 -240
- package/src/plugins/SystemPluginRegistry.ts +0 -242
- package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
- package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
- package/src/plugins/types.ts +0 -129
- package/src/subscription/README.md +0 -132
- package/src/subscription/SubscriptionManager.ts +0 -395
- package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
- package/src/subscription/types.ts +0 -90
- package/src/testing.ts +0 -246
- package/src/utils/BatchingManager.ts +0 -59
- package/src/utils/RerenderLogger.ts +0 -265
- package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
- package/src/utils/__tests__/shallowEqual.test.ts +0 -171
- package/src/utils/__tests__/uuid.test.ts +0 -144
- package/src/utils/generateInstanceId.ts +0 -54
- package/src/utils/shallowEqual.ts +0 -40
- package/src/utils/uuid.ts +0 -22
package/src/Bloc.ts
DELETED
|
@@ -1,180 +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
|
-
> extends BlocBase<S> {
|
|
9
|
-
readonly eventHandlers: Map<
|
|
10
|
-
new (...args: any[]) => A,
|
|
11
|
-
(event: A, emit: (newState: S) => void) => void | Promise<void>
|
|
12
|
-
> = new Map();
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @internal
|
|
16
|
-
* Event queue to ensure sequential processing of async events
|
|
17
|
-
*/
|
|
18
|
-
private _eventQueue: A[] = [];
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @internal
|
|
22
|
-
* Flag indicating if an event is currently being processed
|
|
23
|
-
*/
|
|
24
|
-
private _isProcessingEvent = false;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Registers an event handler for a specific event type.
|
|
28
|
-
* This method is typically called in the constructor of a derived Bloc class.
|
|
29
|
-
* @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent).
|
|
30
|
-
* @param handler A function that processes the event and can emit new states.
|
|
31
|
-
* The 'event' parameter in the handler will be typed to the specific eventConstructor.
|
|
32
|
-
*/
|
|
33
|
-
protected on<E extends A>(
|
|
34
|
-
eventConstructor: new (...args: any[]) => E,
|
|
35
|
-
handler: (event: E, emit: (newState: S) => void) => void | Promise<void>,
|
|
36
|
-
): void {
|
|
37
|
-
if (this.eventHandlers.has(eventConstructor)) {
|
|
38
|
-
Blac.warn(
|
|
39
|
-
`[Bloc ${this._name}:${String(this._id)}] Handler for event '${eventConstructor.name}' already registered. It will be overwritten.`,
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
this.eventHandlers.set(
|
|
43
|
-
eventConstructor,
|
|
44
|
-
handler as (
|
|
45
|
-
event: A,
|
|
46
|
-
emit: (newState: S) => void,
|
|
47
|
-
) => void | Promise<void>,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Dispatches an action/event to the Bloc.
|
|
53
|
-
* Events are queued and processed sequentially to prevent race conditions.
|
|
54
|
-
* @param action The action/event instance to be processed.
|
|
55
|
-
*/
|
|
56
|
-
public add = async (action: A): Promise<void> => {
|
|
57
|
-
// Transform event through plugins
|
|
58
|
-
let transformedAction: A | null = action;
|
|
59
|
-
try {
|
|
60
|
-
transformedAction = (this._plugins as any).transformEvent(action);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('Error transforming event:', error);
|
|
63
|
-
// Continue with original event if transformation fails
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If event was cancelled by plugin, don't process it
|
|
67
|
-
if (transformedAction === null) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Notify bloc plugins of event
|
|
72
|
-
try {
|
|
73
|
-
(this._plugins as any).notifyEvent(transformedAction);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.error('Error notifying plugins of event:', error);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Notify system plugins of event
|
|
79
|
-
Blac.instance.plugins.notifyEventAdded(this as any, transformedAction);
|
|
80
|
-
|
|
81
|
-
this._eventQueue.push(transformedAction);
|
|
82
|
-
|
|
83
|
-
if (!this._isProcessingEvent) {
|
|
84
|
-
await this._processEventQueue();
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @internal
|
|
90
|
-
* Processes events from the queue sequentially
|
|
91
|
-
*/
|
|
92
|
-
private async _processEventQueue(): Promise<void> {
|
|
93
|
-
if (this._isProcessingEvent) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this._isProcessingEvent = true;
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
while (this._eventQueue.length > 0) {
|
|
101
|
-
const action = this._eventQueue.shift()!;
|
|
102
|
-
await this._processEvent(action);
|
|
103
|
-
}
|
|
104
|
-
} finally {
|
|
105
|
-
this._isProcessingEvent = false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* @internal
|
|
111
|
-
* Processes a single event
|
|
112
|
-
*/
|
|
113
|
-
private async _processEvent(action: A): Promise<void> {
|
|
114
|
-
const eventConstructor = action.constructor as new (...args: any[]) => A;
|
|
115
|
-
const handler = this.eventHandlers.get(eventConstructor);
|
|
116
|
-
|
|
117
|
-
if (handler) {
|
|
118
|
-
const emit = (newState: S): void => {
|
|
119
|
-
const previousState = this.state; // State just before this specific emission
|
|
120
|
-
this._pushState(newState, previousState, action);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
await handler(action, emit);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
const constructorName =
|
|
127
|
-
(action.constructor as { name?: string }).name ||
|
|
128
|
-
'UnnamedConstructor';
|
|
129
|
-
const errorContext = {
|
|
130
|
-
blocName: this._name,
|
|
131
|
-
blocId: String(this._id),
|
|
132
|
-
eventType: constructorName,
|
|
133
|
-
currentState: this.state,
|
|
134
|
-
action: action,
|
|
135
|
-
timestamp: new Date().toISOString(),
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
Blac.error(
|
|
139
|
-
`[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${constructorName}':`,
|
|
140
|
-
error,
|
|
141
|
-
'Context:',
|
|
142
|
-
errorContext,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// Notify plugins of the error
|
|
146
|
-
if (error instanceof Error) {
|
|
147
|
-
try {
|
|
148
|
-
this._plugins.notifyError(error, {
|
|
149
|
-
phase: 'event-processing',
|
|
150
|
-
operation: 'handler',
|
|
151
|
-
metadata: { event: action },
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
Blac.instance.plugins.notifyError(error, this as any, {
|
|
155
|
-
phase: 'event-processing',
|
|
156
|
-
operation: 'handler',
|
|
157
|
-
metadata: { event: action },
|
|
158
|
-
});
|
|
159
|
-
} catch (pluginError) {
|
|
160
|
-
console.error('Error notifying plugins:', pluginError);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (error instanceof Error && error.name === 'CriticalError') {
|
|
165
|
-
throw error;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
const constructorName =
|
|
170
|
-
(action.constructor as { name?: string }).name || 'UnnamedConstructor';
|
|
171
|
-
Blac.warn(
|
|
172
|
-
`[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${constructorName}'.`,
|
|
173
|
-
'Registered handlers:',
|
|
174
|
-
Array.from(this.eventHandlers.keys()).map((k) => k.name),
|
|
175
|
-
'Action was:',
|
|
176
|
-
action,
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
package/src/BlocBase.ts
DELETED
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import { generateUUID } from './utils/uuid';
|
|
2
|
-
import { BlocPlugin } from './plugins/types';
|
|
3
|
-
import { BlocPluginRegistry } from './plugins/BlocPluginRegistry';
|
|
4
|
-
import { SubscriptionManager } from './subscription/SubscriptionManager';
|
|
5
|
-
import {
|
|
6
|
-
BlocLifecycleManager,
|
|
7
|
-
BlocLifecycleState,
|
|
8
|
-
StateTransitionResult,
|
|
9
|
-
} from './lifecycle/BlocLifecycle';
|
|
10
|
-
import { BatchingManager } from './utils/BatchingManager';
|
|
11
|
-
import type { Blac } from './Blac';
|
|
12
|
-
|
|
13
|
-
export type BlocInstanceId = string | number | undefined;
|
|
14
|
-
|
|
15
|
-
interface BlocStaticProperties {
|
|
16
|
-
isolated: boolean;
|
|
17
|
-
keepAlive: boolean;
|
|
18
|
-
plugins?: BlocPlugin<any, any>[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Base class for both Blocs and Cubits using unified subscription model.
|
|
23
|
-
*/
|
|
24
|
-
export abstract class BlocBase<S> {
|
|
25
|
-
public uid = generateUUID();
|
|
26
|
-
blacInstance?: Blac;
|
|
27
|
-
|
|
28
|
-
static isolated = false;
|
|
29
|
-
get isIsolated() {
|
|
30
|
-
return this._isolated;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static keepAlive = false;
|
|
34
|
-
get isKeepAlive() {
|
|
35
|
-
return this._keepAlive;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public _isolated = false;
|
|
39
|
-
public _id: BlocInstanceId;
|
|
40
|
-
public _instanceRef?: string;
|
|
41
|
-
public _name: string;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Unified subscription manager for all state notifications
|
|
45
|
-
*/
|
|
46
|
-
public _subscriptionManager: SubscriptionManager<S>;
|
|
47
|
-
|
|
48
|
-
_state: S;
|
|
49
|
-
private _lifecycleManager = new BlocLifecycleManager();
|
|
50
|
-
private _batchingManager = new BatchingManager<S>();
|
|
51
|
-
_keepAlive = false;
|
|
52
|
-
public lastUpdate?: number;
|
|
53
|
-
|
|
54
|
-
_plugins = new BlocPluginRegistry<S, any>();
|
|
55
|
-
|
|
56
|
-
onDispose?: () => void;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Creates a new BlocBase instance with unified subscription management
|
|
60
|
-
*/
|
|
61
|
-
constructor(initialState: S) {
|
|
62
|
-
this._state = initialState;
|
|
63
|
-
this._id = this.constructor.name;
|
|
64
|
-
this._name = this.constructor.name;
|
|
65
|
-
|
|
66
|
-
// Initialize unified subscription system
|
|
67
|
-
this._subscriptionManager = new SubscriptionManager(this as any);
|
|
68
|
-
|
|
69
|
-
// Access static properties
|
|
70
|
-
const Constructor = this.constructor as typeof BlocBase &
|
|
71
|
-
BlocStaticProperties;
|
|
72
|
-
|
|
73
|
-
this._keepAlive =
|
|
74
|
-
typeof Constructor.keepAlive === 'boolean'
|
|
75
|
-
? Constructor.keepAlive
|
|
76
|
-
: false;
|
|
77
|
-
this._isolated =
|
|
78
|
-
typeof Constructor.isolated === 'boolean' ? Constructor.isolated : false;
|
|
79
|
-
|
|
80
|
-
// Initialize plugin registry
|
|
81
|
-
this._plugins = new BlocPluginRegistry<S, any>();
|
|
82
|
-
|
|
83
|
-
// Register static plugins
|
|
84
|
-
if (Constructor.plugins && Array.isArray(Constructor.plugins)) {
|
|
85
|
-
for (const plugin of Constructor.plugins) {
|
|
86
|
-
this._plugins.add(plugin);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Attach all plugins
|
|
91
|
-
this._plugins.attach(this);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Returns the current state
|
|
96
|
-
*/
|
|
97
|
-
get state(): S {
|
|
98
|
-
if (this._lifecycleManager.isDisposed) {
|
|
99
|
-
return this._state; // Return last known state for disposed blocs
|
|
100
|
-
}
|
|
101
|
-
return this._state;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Subscribe to all state changes
|
|
106
|
-
*/
|
|
107
|
-
subscribe(callback: (state: S) => void): () => void {
|
|
108
|
-
return this._subscriptionManager.subscribe({
|
|
109
|
-
type: 'observer',
|
|
110
|
-
notify: (state) => callback(state as S),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Subscribe with a selector for optimized updates
|
|
116
|
-
*/
|
|
117
|
-
subscribeWithSelector<T>(
|
|
118
|
-
selector: (state: S) => T,
|
|
119
|
-
callback: (value: T) => void,
|
|
120
|
-
equalityFn?: (a: T, b: T) => boolean,
|
|
121
|
-
): () => void {
|
|
122
|
-
return this._subscriptionManager.subscribe({
|
|
123
|
-
type: 'consumer',
|
|
124
|
-
selector: selector as any,
|
|
125
|
-
equalityFn: equalityFn as any,
|
|
126
|
-
notify: (value) => callback(value as T),
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Subscribe with React component reference for automatic cleanup
|
|
132
|
-
*/
|
|
133
|
-
subscribeComponent(
|
|
134
|
-
componentRef: WeakRef<object>,
|
|
135
|
-
callback: () => void,
|
|
136
|
-
): () => void {
|
|
137
|
-
return this._subscriptionManager.subscribe({
|
|
138
|
-
type: 'consumer',
|
|
139
|
-
weakRef: componentRef,
|
|
140
|
-
notify: callback,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get current subscription count
|
|
146
|
-
*/
|
|
147
|
-
get subscriptionCount(): number {
|
|
148
|
-
return this._subscriptionManager.size;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Track property access for a subscription
|
|
153
|
-
*/
|
|
154
|
-
trackAccess(subscriptionId: string, path: string, value?: unknown): void {
|
|
155
|
-
this._subscriptionManager.trackAccess(subscriptionId, path, value);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Emit a new state
|
|
160
|
-
*/
|
|
161
|
-
protected emit(newState: S, action?: unknown): void {
|
|
162
|
-
this._pushState(newState, this._state, action);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Internal state push method used by Bloc
|
|
167
|
-
*/
|
|
168
|
-
_pushState(newState: S, oldState: S, action?: unknown): void {
|
|
169
|
-
// Validate state emission conditions
|
|
170
|
-
if (this._lifecycleManager.currentState !== BlocLifecycleState.ACTIVE) {
|
|
171
|
-
this.blacInstance?.error(
|
|
172
|
-
`[${this._name}:${this._id}] Attempted state update on ${this._lifecycleManager.currentState} bloc. Update ignored.`,
|
|
173
|
-
);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (newState === undefined) {
|
|
178
|
-
return; // Silent failure for undefined states
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this._state = newState;
|
|
182
|
-
|
|
183
|
-
// Apply plugins
|
|
184
|
-
const transformedState = this._plugins.transformState(
|
|
185
|
-
oldState,
|
|
186
|
-
newState,
|
|
187
|
-
) as S;
|
|
188
|
-
this._state = transformedState;
|
|
189
|
-
|
|
190
|
-
// Notify plugins of state change
|
|
191
|
-
this._plugins.notifyStateChange(oldState, transformedState);
|
|
192
|
-
|
|
193
|
-
// Notify system plugins of state change
|
|
194
|
-
this.blacInstance?.plugins.notifyStateChanged(
|
|
195
|
-
this as any,
|
|
196
|
-
oldState,
|
|
197
|
-
transformedState,
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// Handle batching
|
|
201
|
-
if (this._batchingManager.isCurrentlyBatching) {
|
|
202
|
-
this._batchingManager.addUpdate({
|
|
203
|
-
newState: transformedState,
|
|
204
|
-
oldState,
|
|
205
|
-
action,
|
|
206
|
-
});
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Notify all subscriptions
|
|
211
|
-
this._subscriptionManager.notify(transformedState, oldState, action);
|
|
212
|
-
this.lastUpdate = Date.now();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
_batchUpdates(callback: () => void): void {
|
|
216
|
-
this._batchingManager.batchUpdates(callback, (finalUpdate) => {
|
|
217
|
-
this._subscriptionManager.notify(
|
|
218
|
-
finalUpdate.newState,
|
|
219
|
-
finalUpdate.oldState,
|
|
220
|
-
finalUpdate.action,
|
|
221
|
-
);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Add a plugin
|
|
227
|
-
*/
|
|
228
|
-
addPlugin(plugin: BlocPlugin<S, any>): void {
|
|
229
|
-
this._plugins.add(plugin);
|
|
230
|
-
|
|
231
|
-
// If plugins are already attached (bloc is active), attach the new plugin
|
|
232
|
-
if ((this._plugins as any).attached && plugin.onAttach) {
|
|
233
|
-
try {
|
|
234
|
-
plugin.onAttach(this);
|
|
235
|
-
} catch (error) {
|
|
236
|
-
console.error(`Plugin '${plugin.name}' error in onAttach:`, error);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Remove a plugin
|
|
243
|
-
*/
|
|
244
|
-
removePlugin(plugin: BlocPlugin<S, any>): void {
|
|
245
|
-
this._plugins.remove(plugin.name);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Get all plugins
|
|
250
|
-
*/
|
|
251
|
-
get plugins(): ReadonlyArray<BlocPlugin<S, any>> {
|
|
252
|
-
return this._plugins.getAll();
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Disposal management
|
|
257
|
-
*/
|
|
258
|
-
get isDisposed(): boolean {
|
|
259
|
-
return this._lifecycleManager.isDisposed;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Atomic state transition for disposal
|
|
264
|
-
*/
|
|
265
|
-
_atomicStateTransition(
|
|
266
|
-
expectedState: BlocLifecycleState,
|
|
267
|
-
newState: BlocLifecycleState,
|
|
268
|
-
): StateTransitionResult {
|
|
269
|
-
return this._lifecycleManager.atomicStateTransition(
|
|
270
|
-
expectedState,
|
|
271
|
-
newState,
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Dispose the bloc and clean up resources
|
|
277
|
-
*/
|
|
278
|
-
async dispose(): Promise<void> {
|
|
279
|
-
const transitionResult = this._lifecycleManager.atomicStateTransition(
|
|
280
|
-
BlocLifecycleState.ACTIVE,
|
|
281
|
-
BlocLifecycleState.DISPOSING,
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
if (!transitionResult.success) {
|
|
285
|
-
if (
|
|
286
|
-
this._lifecycleManager.currentState ===
|
|
287
|
-
BlocLifecycleState.DISPOSAL_REQUESTED
|
|
288
|
-
) {
|
|
289
|
-
this._lifecycleManager.atomicStateTransition(
|
|
290
|
-
BlocLifecycleState.DISPOSAL_REQUESTED,
|
|
291
|
-
BlocLifecycleState.DISPOSING,
|
|
292
|
-
);
|
|
293
|
-
} else {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
// Clear subscriptions
|
|
300
|
-
this._subscriptionManager.clear();
|
|
301
|
-
|
|
302
|
-
// Call disposal hook
|
|
303
|
-
if (this.onDispose) {
|
|
304
|
-
this.onDispose();
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Notify plugins of disposal
|
|
308
|
-
for (const plugin of this._plugins.getAll()) {
|
|
309
|
-
try {
|
|
310
|
-
if ('onDispose' in plugin && typeof plugin.onDispose === 'function') {
|
|
311
|
-
plugin.onDispose(this as any);
|
|
312
|
-
}
|
|
313
|
-
} catch (error) {
|
|
314
|
-
console.error('Plugin disposal error:', error);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Call disposal handler
|
|
319
|
-
const handler = this._lifecycleManager.getDisposalHandler();
|
|
320
|
-
if (handler) {
|
|
321
|
-
handler(this as any);
|
|
322
|
-
}
|
|
323
|
-
} finally {
|
|
324
|
-
this._lifecycleManager.atomicStateTransition(
|
|
325
|
-
BlocLifecycleState.DISPOSING,
|
|
326
|
-
BlocLifecycleState.DISPOSED,
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Schedule disposal when no subscriptions remain
|
|
333
|
-
*/
|
|
334
|
-
_scheduleDisposal(): void {
|
|
335
|
-
const shouldDispose =
|
|
336
|
-
this._subscriptionManager.size === 0 && !this._keepAlive;
|
|
337
|
-
|
|
338
|
-
if (!shouldDispose) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
this._lifecycleManager.scheduleDisposal(
|
|
343
|
-
16,
|
|
344
|
-
() => this._subscriptionManager.size === 0 && !this._keepAlive,
|
|
345
|
-
() => this.dispose(),
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Set disposal handler
|
|
351
|
-
*/
|
|
352
|
-
setDisposalHandler(handler: (bloc: BlocBase<unknown>) => void): void {
|
|
353
|
-
this._lifecycleManager.setDisposalHandler(
|
|
354
|
-
handler as (bloc: unknown) => void,
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Check if disposal should be scheduled (called by subscription manager)
|
|
360
|
-
*/
|
|
361
|
-
checkDisposal(): void {
|
|
362
|
-
if (
|
|
363
|
-
this._subscriptionManager.size === 0 &&
|
|
364
|
-
!this._keepAlive &&
|
|
365
|
-
this._lifecycleManager.currentState === BlocLifecycleState.ACTIVE
|
|
366
|
-
) {
|
|
367
|
-
this._scheduleDisposal();
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Cancel disposal if bloc is in disposal_requested state
|
|
373
|
-
*/
|
|
374
|
-
_cancelDisposalIfRequested(): void {
|
|
375
|
-
this.blacInstance?.log(
|
|
376
|
-
`[${this._name}:${this._id}] _cancelDisposalIfRequested called. Current state: ${this._lifecycleManager.currentState}`,
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
const success = this._lifecycleManager.cancelDisposal();
|
|
380
|
-
|
|
381
|
-
if (success) {
|
|
382
|
-
this.blacInstance?.log(
|
|
383
|
-
`[${this._name}:${this._id}] Disposal cancelled - new subscription added`,
|
|
384
|
-
);
|
|
385
|
-
} else if (
|
|
386
|
-
this._lifecycleManager.currentState ===
|
|
387
|
-
BlocLifecycleState.DISPOSAL_REQUESTED
|
|
388
|
-
) {
|
|
389
|
-
this.blacInstance?.warn(
|
|
390
|
-
`[${this._name}:${this._id}] Failed to cancel disposal. Current state: ${this._lifecycleManager.currentState}`,
|
|
391
|
-
);
|
|
392
|
-
} else if (this._lifecycleManager.isDisposed) {
|
|
393
|
-
this.blacInstance?.error(
|
|
394
|
-
`[${this._name}:${this._id}] Cannot cancel disposal - bloc is already disposed`,
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
package/src/Cubit.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { Blac } from './Blac';
|
|
2
|
-
import { BlocBase } from './BlocBase';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A Cubit is a simpler version of a Bloc that doesn't handle events.
|
|
6
|
-
* It manages state and provides methods to update it.
|
|
7
|
-
* @template S - The type of state this Cubit manages
|
|
8
|
-
*/
|
|
9
|
-
export abstract class Cubit<S> extends BlocBase<S> {
|
|
10
|
-
/**
|
|
11
|
-
* Updates the current state and notifies all observers of the change.
|
|
12
|
-
* If the new state is identical to the current state (using Object.is),
|
|
13
|
-
* no update will occur.
|
|
14
|
-
* @param state - The new state to set
|
|
15
|
-
*/
|
|
16
|
-
emit(state: S): void {
|
|
17
|
-
if (Object.is(state, this.state)) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const oldState = this.state;
|
|
22
|
-
const newState = state;
|
|
23
|
-
this._pushState(newState, oldState);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Partially updates the current state by merging it with the provided state patch.
|
|
28
|
-
* This method is only applicable when the state is an object type.
|
|
29
|
-
*
|
|
30
|
-
* @param statePatch - A partial state object containing only the properties to update
|
|
31
|
-
* @param ignoreChangeCheck - If true, skips checking if the state has actually changed
|
|
32
|
-
* @throws {TypeError} If the state is not an object type
|
|
33
|
-
*/
|
|
34
|
-
patch(
|
|
35
|
-
statePatch: S extends object ? Partial<S> : S,
|
|
36
|
-
ignoreChangeCheck = false,
|
|
37
|
-
): void {
|
|
38
|
-
if (typeof this.state !== 'object' || this.state === null) {
|
|
39
|
-
Blac.warn(
|
|
40
|
-
'Cubit.patch: was called on a cubit where the state is not an object. This is a no-op.',
|
|
41
|
-
);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let changes = false;
|
|
46
|
-
if (!ignoreChangeCheck) {
|
|
47
|
-
for (const key in statePatch) {
|
|
48
|
-
if (Object.prototype.hasOwnProperty.call(statePatch, key)) {
|
|
49
|
-
const s = this.state;
|
|
50
|
-
const current = s[key as keyof S];
|
|
51
|
-
if (!Object.is(statePatch[key as keyof S], current)) {
|
|
52
|
-
changes = true;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
changes = true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (changes) {
|
|
62
|
-
this.emit({
|
|
63
|
-
...this.state,
|
|
64
|
-
...(statePatch as Partial<S>),
|
|
65
|
-
} as S);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|