@ai.ntellect/core 0.7.8 → 1.0.0
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 +158 -81
- package/index.ts +462 -22
- package/package copy.json +21 -0
- package/package.json +9 -44
- package/tsconfig.json +108 -22
- package/types.ts +62 -0
- package/utils/executor.ts +42 -0
- package/.mocharc.json +0 -5
- package/dist/graph/controller.d.ts +0 -31
- package/dist/graph/controller.d.ts.map +0 -1
- package/dist/graph/controller.js +0 -71
- package/dist/graph/controller.js.map +0 -1
- package/dist/graph/event-manager.d.ts +0 -93
- package/dist/graph/event-manager.d.ts.map +0 -1
- package/dist/graph/event-manager.js +0 -296
- package/dist/graph/event-manager.js.map +0 -1
- package/dist/graph/index.d.ts +0 -159
- package/dist/graph/index.d.ts.map +0 -1
- package/dist/graph/index.js +0 -303
- package/dist/graph/index.js.map +0 -1
- package/dist/graph/logger.d.ts +0 -46
- package/dist/graph/logger.d.ts.map +0 -1
- package/dist/graph/logger.js +0 -69
- package/dist/graph/logger.js.map +0 -1
- package/dist/graph/node.d.ts +0 -93
- package/dist/graph/node.d.ts.map +0 -1
- package/dist/graph/node.js +0 -259
- package/dist/graph/node.js.map +0 -1
- package/dist/graph/observer.d.ts +0 -115
- package/dist/graph/observer.d.ts.map +0 -1
- package/dist/graph/observer.js +0 -198
- package/dist/graph/observer.js.map +0 -1
- package/dist/index.d.ts +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -42
- package/dist/index.js.map +0 -1
- package/dist/interfaces/index.d.ts +0 -447
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js +0 -75
- package/dist/interfaces/index.js.map +0 -1
- package/dist/modules/agenda/adapters/node-cron/index.d.ts +0 -17
- package/dist/modules/agenda/adapters/node-cron/index.d.ts.map +0 -1
- package/dist/modules/agenda/adapters/node-cron/index.js +0 -30
- package/dist/modules/agenda/adapters/node-cron/index.js.map +0 -1
- package/dist/modules/agenda/index.d.ts +0 -63
- package/dist/modules/agenda/index.d.ts.map +0 -1
- package/dist/modules/agenda/index.js +0 -141
- package/dist/modules/agenda/index.js.map +0 -1
- package/dist/modules/embedding/adapters/ai/index.d.ts +0 -29
- package/dist/modules/embedding/adapters/ai/index.d.ts.map +0 -1
- package/dist/modules/embedding/adapters/ai/index.js +0 -58
- package/dist/modules/embedding/adapters/ai/index.js.map +0 -1
- package/dist/modules/embedding/index.d.ts +0 -36
- package/dist/modules/embedding/index.d.ts.map +0 -1
- package/dist/modules/embedding/index.js +0 -60
- package/dist/modules/embedding/index.js.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.d.ts +0 -120
- package/dist/modules/memory/adapters/in-memory/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.js +0 -211
- package/dist/modules/memory/adapters/in-memory/index.js.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.d.ts +0 -110
- package/dist/modules/memory/adapters/meilisearch/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.js +0 -321
- package/dist/modules/memory/adapters/meilisearch/index.js.map +0 -1
- package/dist/modules/memory/adapters/redis/index.d.ts +0 -82
- package/dist/modules/memory/adapters/redis/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/redis/index.js +0 -159
- package/dist/modules/memory/adapters/redis/index.js.map +0 -1
- package/dist/modules/memory/index.d.ts +0 -67
- package/dist/modules/memory/index.d.ts.map +0 -1
- package/dist/modules/memory/index.js +0 -104
- package/dist/modules/memory/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -170
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/generate-action-schema.d.ts +0 -5
- package/dist/utils/generate-action-schema.d.ts.map +0 -1
- package/dist/utils/generate-action-schema.js +0 -44
- package/dist/utils/generate-action-schema.js.map +0 -1
- package/dist/utils/header-builder.d.ts +0 -12
- package/dist/utils/header-builder.d.ts.map +0 -1
- package/dist/utils/header-builder.js +0 -35
- package/dist/utils/header-builder.js.map +0 -1
- package/graph/controller.ts +0 -74
- package/graph/event-manager.ts +0 -363
- package/graph/index.ts +0 -395
- package/graph/logger.ts +0 -70
- package/graph/node.ts +0 -327
- package/graph/observer.ts +0 -368
- package/interfaces/index.ts +0 -548
- package/modules/agenda/adapters/node-cron/index.ts +0 -25
- package/modules/agenda/index.ts +0 -146
- package/modules/embedding/adapters/ai/index.ts +0 -42
- package/modules/embedding/index.ts +0 -45
- package/modules/memory/adapters/in-memory/index.ts +0 -207
- package/modules/memory/adapters/meilisearch/index.ts +0 -361
- package/modules/memory/adapters/redis/index.ts +0 -164
- package/modules/memory/index.ts +0 -93
- package/test/graph/controller.test.ts +0 -187
- package/test/graph/event-manager.test.ts +0 -118
- package/test/graph/index.test.ts +0 -684
- package/test/graph/node.test.ts +0 -655
- package/test/graph/observer.test.ts +0 -398
- package/test/modules/agenda/node-cron.test.ts +0 -307
- package/test/modules/memory/adapters/in-memory.test.ts +0 -153
- package/test/modules/memory/adapters/meilisearch.test.ts +0 -287
- package/test/modules/memory/base.test.ts +0 -230
- package/types/index.ts +0 -190
- package/utils/generate-action-schema.ts +0 -46
- package/utils/header-builder.ts +0 -40
package/graph/node.ts
DELETED
@@ -1,327 +0,0 @@
|
|
1
|
-
import { IEventEmitter } from "interfaces";
|
2
|
-
import { BehaviorSubject, Subject } from "rxjs";
|
3
|
-
import { ZodSchema } from "zod";
|
4
|
-
import { GraphContext, GraphEvent, Node } from "../types";
|
5
|
-
import { GraphEventManager } from "./event-manager";
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Represents a node in the graph that can execute operations and manage state
|
9
|
-
* @template T - The Zod schema type for validation
|
10
|
-
*/
|
11
|
-
export interface NodeParams<T = any> {
|
12
|
-
[key: string]: T;
|
13
|
-
}
|
14
|
-
|
15
|
-
export interface GraphLogger {
|
16
|
-
addLog: (message: string, data?: any) => void;
|
17
|
-
}
|
18
|
-
|
19
|
-
export class GraphNode<T extends ZodSchema> {
|
20
|
-
private lastStateEvent: GraphEvent<T> | null = null;
|
21
|
-
private eventEmitter: IEventEmitter;
|
22
|
-
|
23
|
-
/**
|
24
|
-
* Creates a new GraphNode instance
|
25
|
-
* @param nodes - Map of all nodes in the graph
|
26
|
-
* @param logger - Logger instance for tracking node operations
|
27
|
-
* @param eventManager - Manager for handling graph events
|
28
|
-
* @param eventSubject - Subject for emitting events
|
29
|
-
* @param stateSubject - Subject for managing graph state
|
30
|
-
*/
|
31
|
-
constructor(
|
32
|
-
private nodes: Map<string, Node<T, any>>,
|
33
|
-
private logger: GraphLogger,
|
34
|
-
private eventManager: GraphEventManager<T>,
|
35
|
-
private eventSubject: Subject<GraphEvent<T>>,
|
36
|
-
private stateSubject: BehaviorSubject<GraphContext<T>>
|
37
|
-
) {
|
38
|
-
this.eventEmitter = eventManager["eventEmitter"];
|
39
|
-
}
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Emits an event with the specified type and payload
|
43
|
-
* @param type - The type of event to emit
|
44
|
-
* @param payload - The data associated with the event
|
45
|
-
* @private
|
46
|
-
*/
|
47
|
-
private emitEvent(type: string, payload: any) {
|
48
|
-
if (type === "nodeStateChanged") {
|
49
|
-
if (
|
50
|
-
this.lastStateEvent?.type === type &&
|
51
|
-
this.lastStateEvent.payload.property === payload.property &&
|
52
|
-
this.lastStateEvent.payload.newValue === payload.newValue &&
|
53
|
-
this.lastStateEvent.payload.nodeName === payload.nodeName
|
54
|
-
) {
|
55
|
-
return;
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
const event = {
|
60
|
-
type,
|
61
|
-
payload: {
|
62
|
-
...payload,
|
63
|
-
name: type === "nodeStateChanged" ? payload.nodeName : payload.name,
|
64
|
-
context: { ...payload.context },
|
65
|
-
},
|
66
|
-
timestamp: Date.now(),
|
67
|
-
};
|
68
|
-
|
69
|
-
this.eventSubject.next(event);
|
70
|
-
this.eventManager.emitEvent(type, event);
|
71
|
-
|
72
|
-
if (type === "nodeStateChanged") {
|
73
|
-
this.lastStateEvent = event;
|
74
|
-
this.stateSubject.next({ ...payload.context });
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
/**
|
79
|
-
* Executes a node with the given name and context
|
80
|
-
* @param nodeName - The name of the node to execute
|
81
|
-
* @param context - The current graph context
|
82
|
-
* @param params - Input data for the node
|
83
|
-
* @param triggeredByEvent - Whether the execution was triggered by an event
|
84
|
-
* @throws Error if the node is not found or execution fails
|
85
|
-
*/
|
86
|
-
public async executeNode(
|
87
|
-
nodeName: string,
|
88
|
-
context: GraphContext<T>,
|
89
|
-
params: any,
|
90
|
-
triggeredByEvent: boolean = false
|
91
|
-
): Promise<void> {
|
92
|
-
const node = this.nodes.get(nodeName);
|
93
|
-
if (!node) throw new Error(`Node "${nodeName}" not found.`);
|
94
|
-
|
95
|
-
const nodeContext = { ...context };
|
96
|
-
this.emitEvent("nodeStarted", { name: nodeName, context: nodeContext });
|
97
|
-
|
98
|
-
try {
|
99
|
-
if (node.correlateEvents) {
|
100
|
-
await this.eventManager.waitForCorrelatedEvents(
|
101
|
-
node.correlateEvents.events,
|
102
|
-
node.correlateEvents.timeout || 30000,
|
103
|
-
(events) => {
|
104
|
-
return node.correlateEvents!.correlation(events);
|
105
|
-
}
|
106
|
-
);
|
107
|
-
}
|
108
|
-
|
109
|
-
// Ensuite, attendre les événements si waitForEvents est défini
|
110
|
-
if (node.waitForEvents) {
|
111
|
-
await this.eventManager.waitForEvents(
|
112
|
-
node.waitForEvents.events,
|
113
|
-
node.waitForEvents.timeout
|
114
|
-
);
|
115
|
-
}
|
116
|
-
|
117
|
-
const contextProxy = new Proxy(nodeContext, {
|
118
|
-
set: (target: any, prop: string | symbol, value: any) => {
|
119
|
-
const oldValue = target[prop.toString()];
|
120
|
-
if (oldValue === value) return true;
|
121
|
-
|
122
|
-
target[prop.toString()] = value;
|
123
|
-
// Mettre à jour le contexte global
|
124
|
-
context[prop.toString() as keyof typeof context] = value;
|
125
|
-
|
126
|
-
this.emitEvent("nodeStateChanged", {
|
127
|
-
nodeName,
|
128
|
-
property: prop.toString(),
|
129
|
-
oldValue,
|
130
|
-
newValue: value,
|
131
|
-
context: { ...target },
|
132
|
-
});
|
133
|
-
return true;
|
134
|
-
},
|
135
|
-
});
|
136
|
-
|
137
|
-
if (node.condition && !node.condition(contextProxy, params)) {
|
138
|
-
return;
|
139
|
-
}
|
140
|
-
|
141
|
-
await this.executeWithRetry(node, contextProxy, nodeName, params);
|
142
|
-
this.emitEvent("nodeCompleted", { name: nodeName, context: nodeContext });
|
143
|
-
|
144
|
-
if (!triggeredByEvent && node.next) {
|
145
|
-
const nextNodes =
|
146
|
-
typeof node.next === "function" ? node.next(contextProxy) : node.next;
|
147
|
-
for (const nextNodeName of nextNodes) {
|
148
|
-
await this.executeNode(nextNodeName, context, undefined, false);
|
149
|
-
}
|
150
|
-
}
|
151
|
-
} catch (error) {
|
152
|
-
this.emitEvent("nodeError", {
|
153
|
-
name: nodeName,
|
154
|
-
error,
|
155
|
-
context: nodeContext,
|
156
|
-
});
|
157
|
-
throw error;
|
158
|
-
}
|
159
|
-
}
|
160
|
-
|
161
|
-
/**
|
162
|
-
* Validates the params for a node using its schema
|
163
|
-
* @param node - The node whose params need validation
|
164
|
-
* @param params - The input data to validate
|
165
|
-
* @param nodeName - The name of the node (for error messages)
|
166
|
-
* @throws Error if validation fails
|
167
|
-
* @private
|
168
|
-
*/
|
169
|
-
private async validateParams(
|
170
|
-
node: Node<T, any>,
|
171
|
-
params: any,
|
172
|
-
nodeName: string
|
173
|
-
): Promise<void> {
|
174
|
-
// Si pas de schéma de validation ou si le schéma est optionnel, accepter n'importe quels params
|
175
|
-
if (!node.params || node.params.isOptional?.()) return;
|
176
|
-
|
177
|
-
// Vérifier les params uniquement si un schéma est défini et non optionnel
|
178
|
-
if (!params) {
|
179
|
-
throw new Error(`Params required for node "${nodeName}"`);
|
180
|
-
}
|
181
|
-
|
182
|
-
try {
|
183
|
-
return node.params.parse(params);
|
184
|
-
} catch (error: any) {
|
185
|
-
throw error;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
/**
|
190
|
-
* Handles event-related operations for a node
|
191
|
-
* @param node - The node whose events need handling
|
192
|
-
* @param nodeName - The name of the node
|
193
|
-
* @param context - The current graph context
|
194
|
-
* @private
|
195
|
-
*/
|
196
|
-
private async handleEvents(
|
197
|
-
node: Node<T, any>,
|
198
|
-
nodeName: string,
|
199
|
-
context: GraphContext<T>
|
200
|
-
): Promise<void> {
|
201
|
-
if (node.correlateEvents) {
|
202
|
-
await this.handleCorrelatedEvents(node, nodeName);
|
203
|
-
}
|
204
|
-
|
205
|
-
if (node.waitForEvents) {
|
206
|
-
await this.handleWaitForEvents(node, nodeName);
|
207
|
-
}
|
208
|
-
}
|
209
|
-
|
210
|
-
/**
|
211
|
-
* Executes a node with retry logic
|
212
|
-
* @param node - The node to execute
|
213
|
-
* @param contextProxy - The proxied graph context
|
214
|
-
* @param params - Input data for the node
|
215
|
-
* @param nodeName - The name of the node
|
216
|
-
* @param params - Parameters for the node
|
217
|
-
* @throws Error if all retry attempts fail
|
218
|
-
* @private
|
219
|
-
*/
|
220
|
-
private async executeWithRetry(
|
221
|
-
node: Node<T, any>,
|
222
|
-
contextProxy: GraphContext<T>,
|
223
|
-
nodeName: string,
|
224
|
-
params?: NodeParams
|
225
|
-
): Promise<void> {
|
226
|
-
let attempts = 0;
|
227
|
-
let lastError: Error = new Error("Unknown error");
|
228
|
-
|
229
|
-
while (attempts < (node.retry?.maxAttempts || 1)) {
|
230
|
-
try {
|
231
|
-
if (node.params) {
|
232
|
-
await this.validateParams(node, params, nodeName);
|
233
|
-
}
|
234
|
-
|
235
|
-
await node.execute(contextProxy, params, {
|
236
|
-
eventEmitter: this.eventEmitter,
|
237
|
-
});
|
238
|
-
return;
|
239
|
-
} catch (error: any) {
|
240
|
-
lastError =
|
241
|
-
error instanceof Error
|
242
|
-
? error
|
243
|
-
: new Error(error?.message || "Unknown error");
|
244
|
-
attempts++;
|
245
|
-
|
246
|
-
if (attempts === (node.retry?.maxAttempts || 1)) {
|
247
|
-
if (node.retry?.onRetryFailed) {
|
248
|
-
await node.retry.onRetryFailed(lastError, contextProxy);
|
249
|
-
if (node.retry.continueOnFailed) return;
|
250
|
-
}
|
251
|
-
throw lastError;
|
252
|
-
}
|
253
|
-
|
254
|
-
await new Promise((resolve) =>
|
255
|
-
setTimeout(resolve, node.retry?.delay || 0)
|
256
|
-
);
|
257
|
-
}
|
258
|
-
}
|
259
|
-
}
|
260
|
-
|
261
|
-
/**
|
262
|
-
* Handles correlated events for a node
|
263
|
-
* @param node - The node with correlated events
|
264
|
-
* @param nodeName - The name of the node
|
265
|
-
* @throws Error if correlation fails or timeout occurs
|
266
|
-
* @private
|
267
|
-
*/
|
268
|
-
private async handleCorrelatedEvents(
|
269
|
-
node: Node<T, any>,
|
270
|
-
nodeName: string
|
271
|
-
): Promise<void> {
|
272
|
-
if (node.correlateEvents) {
|
273
|
-
const { events, timeout, correlation } = node.correlateEvents;
|
274
|
-
this.logger.addLog(
|
275
|
-
`⏳ Node "${nodeName}" waiting for correlated events: ${events.join(
|
276
|
-
", "
|
277
|
-
)}`
|
278
|
-
);
|
279
|
-
|
280
|
-
try {
|
281
|
-
// Attendre les événements
|
282
|
-
const receivedEvents = await this.eventManager.waitForEvents(
|
283
|
-
events,
|
284
|
-
timeout
|
285
|
-
);
|
286
|
-
|
287
|
-
// Vérifier la corrélation
|
288
|
-
if (!correlation(receivedEvents)) {
|
289
|
-
this.logger.addLog(
|
290
|
-
`❌ Event correlation failed for node "${nodeName}"`
|
291
|
-
);
|
292
|
-
throw new Error(`Event correlation failed for node "${nodeName}"`);
|
293
|
-
}
|
294
|
-
|
295
|
-
this.logger.addLog(
|
296
|
-
`✅ Event correlation succeeded for node "${nodeName}"`
|
297
|
-
);
|
298
|
-
} catch (error) {
|
299
|
-
this.logger.addLog(
|
300
|
-
`❌ Error waiting for events: ${(error as Error).message}`
|
301
|
-
);
|
302
|
-
throw error;
|
303
|
-
}
|
304
|
-
}
|
305
|
-
}
|
306
|
-
|
307
|
-
/**
|
308
|
-
* Handles waiting for events
|
309
|
-
* @param node - The node waiting for events
|
310
|
-
* @param nodeName - The name of the node
|
311
|
-
* @throws Error if timeout occurs
|
312
|
-
* @private
|
313
|
-
*/
|
314
|
-
private async handleWaitForEvents(
|
315
|
-
node: Node<T, any>,
|
316
|
-
nodeName: string
|
317
|
-
): Promise<void> {
|
318
|
-
if (node.waitForEvents) {
|
319
|
-
const { events, timeout } = node.waitForEvents;
|
320
|
-
this.logger.addLog(
|
321
|
-
`⏳ Node "${nodeName}" waiting for events: ${events.join(", ")}`
|
322
|
-
);
|
323
|
-
await this.eventManager.waitForEvents(events, timeout);
|
324
|
-
this.logger.addLog(`✅ All events received for node "${nodeName}"`);
|
325
|
-
}
|
326
|
-
}
|
327
|
-
}
|
package/graph/observer.ts
DELETED
@@ -1,368 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
BehaviorSubject,
|
3
|
-
Observable,
|
4
|
-
Subject,
|
5
|
-
combineLatest,
|
6
|
-
firstValueFrom,
|
7
|
-
} from "rxjs";
|
8
|
-
import {
|
9
|
-
debounceTime,
|
10
|
-
distinctUntilChanged,
|
11
|
-
filter,
|
12
|
-
map,
|
13
|
-
share,
|
14
|
-
startWith,
|
15
|
-
take,
|
16
|
-
takeUntil,
|
17
|
-
timeout,
|
18
|
-
} from "rxjs/operators";
|
19
|
-
import { ZodSchema } from "zod";
|
20
|
-
import { GraphObservable } from "../interfaces";
|
21
|
-
import { GraphContext, GraphEvent } from "../types";
|
22
|
-
import { GraphFlow } from "./index";
|
23
|
-
|
24
|
-
interface ObserverOptions {
|
25
|
-
debounce?: number;
|
26
|
-
delay?: number;
|
27
|
-
stream?: boolean;
|
28
|
-
properties?: (string | number)[]; // Accepte uniquement string ou number comme clés
|
29
|
-
onStreamLetter?: (data: { letter: string; property: string }) => void;
|
30
|
-
onStreamComplete?: () => void;
|
31
|
-
}
|
32
|
-
|
33
|
-
/**
|
34
|
-
* GraphObserver class provides reactive observation capabilities for a GraphFlow instance
|
35
|
-
* It allows monitoring state changes, node updates, and specific events in the graph
|
36
|
-
* @template T - The Zod schema type that defines the structure of the graph data
|
37
|
-
*/
|
38
|
-
export class GraphObserver<T extends ZodSchema> {
|
39
|
-
constructor(
|
40
|
-
private graph: GraphFlow<T>,
|
41
|
-
private eventSubject: Subject<GraphEvent<T>>,
|
42
|
-
private stateSubject: BehaviorSubject<GraphContext<T>>,
|
43
|
-
private destroySubject: Subject<void>
|
44
|
-
) {}
|
45
|
-
|
46
|
-
/**
|
47
|
-
* Observes the entire graph state changes
|
48
|
-
* @param options Configuration options for the observation
|
49
|
-
* @param options.debounce Debounce time in milliseconds
|
50
|
-
* @param options.delay Delay between emissions in milliseconds
|
51
|
-
* @param options.stream If true, streams the specified properties letter by letter
|
52
|
-
* @param options.properties List of properties to stream
|
53
|
-
* @param options.onStreamLetter Callback for each letter emitted during streaming
|
54
|
-
* @param options.onStreamComplete Callback when streaming is complete
|
55
|
-
* @returns An Observable that emits the complete graph context whenever it changes
|
56
|
-
*/
|
57
|
-
state(options: ObserverOptions = {}): GraphObservable<T> {
|
58
|
-
const baseObservable = new Observable<any>((subscriber) => {
|
59
|
-
const subscription = combineLatest([
|
60
|
-
this.eventSubject.pipe(
|
61
|
-
filter((event) => event.type === "nodeStateChanged"),
|
62
|
-
map((event) => event.payload.context),
|
63
|
-
distinctUntilChanged(
|
64
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
65
|
-
),
|
66
|
-
debounceTime(options.debounce || 100)
|
67
|
-
),
|
68
|
-
this.stateSubject,
|
69
|
-
])
|
70
|
-
.pipe(
|
71
|
-
map(([eventContext, stateContext]) => ({
|
72
|
-
...stateContext,
|
73
|
-
...eventContext,
|
74
|
-
})),
|
75
|
-
distinctUntilChanged(
|
76
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
77
|
-
)
|
78
|
-
)
|
79
|
-
.subscribe(subscriber);
|
80
|
-
|
81
|
-
// Stream the specified properties if specified
|
82
|
-
if (options.stream && options.properties) {
|
83
|
-
const context = this.stateSubject.getValue();
|
84
|
-
options.properties.forEach((property) => {
|
85
|
-
const message = context[property];
|
86
|
-
if (message) {
|
87
|
-
this.streamMessage(
|
88
|
-
message.toString(),
|
89
|
-
500,
|
90
|
-
property as string
|
91
|
-
).subscribe({
|
92
|
-
next: (data) => options.onStreamLetter?.(data),
|
93
|
-
complete: () => options.onStreamComplete?.(),
|
94
|
-
});
|
95
|
-
}
|
96
|
-
});
|
97
|
-
}
|
98
|
-
|
99
|
-
return () => subscription.unsubscribe();
|
100
|
-
});
|
101
|
-
|
102
|
-
// Extend the observable with our custom methods
|
103
|
-
return Object.assign(baseObservable, {
|
104
|
-
state: () => this.stateSubject.asObservable(),
|
105
|
-
node: (nodeName: string) =>
|
106
|
-
this.stateSubject.pipe(map((state) => ({ ...state, nodeName }))),
|
107
|
-
nodes: (nodeNames: string[]) =>
|
108
|
-
this.eventSubject.pipe(
|
109
|
-
filter(
|
110
|
-
(event) =>
|
111
|
-
event.type === "nodeStateChanged" &&
|
112
|
-
nodeNames.includes(event.payload?.name ?? "")
|
113
|
-
),
|
114
|
-
map((event) => event.payload.context),
|
115
|
-
distinctUntilChanged(
|
116
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
117
|
-
),
|
118
|
-
takeUntil(this.destroySubject),
|
119
|
-
share()
|
120
|
-
),
|
121
|
-
property: (props: string | string[]) =>
|
122
|
-
this.stateSubject.pipe(
|
123
|
-
map((state) => {
|
124
|
-
const properties = Array.isArray(props) ? props : [props];
|
125
|
-
return properties.reduce(
|
126
|
-
(acc, prop) => ({
|
127
|
-
...acc,
|
128
|
-
[prop]: state[prop],
|
129
|
-
}),
|
130
|
-
{}
|
131
|
-
);
|
132
|
-
})
|
133
|
-
),
|
134
|
-
event: (eventName: string) =>
|
135
|
-
this.eventSubject.pipe(filter((event) => event.type === eventName)),
|
136
|
-
until: (
|
137
|
-
observable: Observable<any>,
|
138
|
-
predicate: (state: any) => boolean
|
139
|
-
) => firstValueFrom(observable.pipe(filter(predicate), take(1))),
|
140
|
-
}) as GraphObservable<T>;
|
141
|
-
}
|
142
|
-
|
143
|
-
/**
|
144
|
-
* Observes state changes for a specific node
|
145
|
-
* @param name - The name of the node to observe
|
146
|
-
* @returns An Observable that emits the graph context when the specified node changes
|
147
|
-
*/
|
148
|
-
node(name: string): Observable<GraphContext<T>> {
|
149
|
-
return this.eventSubject.pipe(
|
150
|
-
filter(
|
151
|
-
(event) =>
|
152
|
-
event.type === "nodeStateChanged" && event.payload?.name === name
|
153
|
-
),
|
154
|
-
map((event) => event.payload.context),
|
155
|
-
distinctUntilChanged(
|
156
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
157
|
-
),
|
158
|
-
takeUntil(this.destroySubject),
|
159
|
-
share()
|
160
|
-
);
|
161
|
-
}
|
162
|
-
|
163
|
-
/**
|
164
|
-
* Observes state changes for multiple nodes
|
165
|
-
* @param names - Array of node names to observe
|
166
|
-
* @returns An Observable that emits the graph context when any of the specified nodes change
|
167
|
-
*/
|
168
|
-
nodes(names: string[]): Observable<GraphContext<T>> {
|
169
|
-
return this.eventSubject.pipe(
|
170
|
-
filter(
|
171
|
-
(event) =>
|
172
|
-
names.includes(event.payload?.name ?? "") &&
|
173
|
-
event.type === "nodeStateChanged"
|
174
|
-
),
|
175
|
-
map(() => this.graph.getContext()),
|
176
|
-
distinctUntilChanged(
|
177
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
178
|
-
),
|
179
|
-
takeUntil(this.destroySubject),
|
180
|
-
share()
|
181
|
-
);
|
182
|
-
}
|
183
|
-
|
184
|
-
/**
|
185
|
-
* Observes specific properties of the graph context
|
186
|
-
* @param keys - Single or multiple property keys to observe
|
187
|
-
* @returns An Observable that emits an object containing only the specified properties
|
188
|
-
* @template K - The key of the property to observe from GraphContext<T>
|
189
|
-
*/
|
190
|
-
property<K extends keyof GraphContext<T>>(
|
191
|
-
keys: K | K[]
|
192
|
-
): Observable<{ [P in K]: GraphContext<T>[P] } & { name: string }> {
|
193
|
-
const properties = Array.isArray(keys) ? keys : [keys];
|
194
|
-
|
195
|
-
return this.eventSubject.pipe(
|
196
|
-
filter(
|
197
|
-
(event) =>
|
198
|
-
event.type === "nodeStateChanged" &&
|
199
|
-
properties.some((key) => event.payload?.property === key)
|
200
|
-
),
|
201
|
-
map((event) => ({
|
202
|
-
...properties.reduce(
|
203
|
-
(obj, key) => ({
|
204
|
-
...obj,
|
205
|
-
[key]: event.payload.context[key],
|
206
|
-
}),
|
207
|
-
{} as { [P in K]: GraphContext<T>[P] }
|
208
|
-
),
|
209
|
-
name: event.payload.name as string,
|
210
|
-
})),
|
211
|
-
startWith({
|
212
|
-
...properties.reduce(
|
213
|
-
(obj, key) => ({
|
214
|
-
...obj,
|
215
|
-
[key]: this.stateSubject.value[key],
|
216
|
-
}),
|
217
|
-
{}
|
218
|
-
),
|
219
|
-
name: "initial",
|
220
|
-
} as { [P in K]: GraphContext<T>[P] } & { name: string }),
|
221
|
-
distinctUntilChanged(
|
222
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
223
|
-
),
|
224
|
-
share()
|
225
|
-
);
|
226
|
-
}
|
227
|
-
|
228
|
-
/**
|
229
|
-
* Observes specific events in the graph
|
230
|
-
* @param type - The type of event to observe
|
231
|
-
* @returns An Observable that emits events of the specified type
|
232
|
-
*/
|
233
|
-
event(type: string): Observable<GraphEvent<T>> {
|
234
|
-
return this.eventSubject.pipe(
|
235
|
-
filter((event) => event.type === type),
|
236
|
-
map((event) => event),
|
237
|
-
takeUntil(this.destroySubject),
|
238
|
-
share()
|
239
|
-
);
|
240
|
-
}
|
241
|
-
|
242
|
-
/**
|
243
|
-
* Waits for a specific condition to be met on an observable
|
244
|
-
* @param observable - The Observable to watch
|
245
|
-
* @param predicate - A function that returns true when the desired condition is met
|
246
|
-
* @returns A Promise that resolves with the value when the predicate returns true
|
247
|
-
* @template R - The type of value emitted by the observable
|
248
|
-
*/
|
249
|
-
until<R>(
|
250
|
-
observable: Observable<R>,
|
251
|
-
predicate: (value: R) => boolean
|
252
|
-
): Promise<R> {
|
253
|
-
return new Promise((resolve) => {
|
254
|
-
const subscription = observable.subscribe({
|
255
|
-
next: (value) => {
|
256
|
-
if (predicate(value)) {
|
257
|
-
subscription.unsubscribe();
|
258
|
-
resolve(value);
|
259
|
-
}
|
260
|
-
},
|
261
|
-
});
|
262
|
-
});
|
263
|
-
}
|
264
|
-
|
265
|
-
/**
|
266
|
-
* Waits for correlated events to occur and validates them using a correlation function
|
267
|
-
* @param eventTypes - Array of event types to wait for
|
268
|
-
* @param timeoutMs - Timeout duration in milliseconds
|
269
|
-
* @param correlationFn - Function to validate the correlation between events
|
270
|
-
* @returns Promise that resolves when all correlated events are received
|
271
|
-
*/
|
272
|
-
waitForCorrelatedEvents(
|
273
|
-
eventTypes: string[],
|
274
|
-
timeoutMs: number,
|
275
|
-
correlationFn: (events: GraphEvent<T>[]) => boolean
|
276
|
-
): Promise<GraphEvent<T>[]> {
|
277
|
-
return new Promise((resolve, reject) => {
|
278
|
-
const eventObservables = eventTypes.map((eventType) =>
|
279
|
-
this.eventSubject.pipe(
|
280
|
-
filter((event): event is GraphEvent<T> => {
|
281
|
-
return event.type === eventType && "timestamp" in event;
|
282
|
-
}),
|
283
|
-
take(1)
|
284
|
-
)
|
285
|
-
);
|
286
|
-
|
287
|
-
combineLatest(eventObservables)
|
288
|
-
.pipe(timeout(timeoutMs), take(1))
|
289
|
-
.subscribe({
|
290
|
-
next: (events) => {
|
291
|
-
if (correlationFn(events)) {
|
292
|
-
resolve(events);
|
293
|
-
} else {
|
294
|
-
reject(new Error(`Correlation validation failed`));
|
295
|
-
}
|
296
|
-
},
|
297
|
-
error: (error) => reject(error),
|
298
|
-
});
|
299
|
-
});
|
300
|
-
}
|
301
|
-
|
302
|
-
/**
|
303
|
-
* Observes the current state of the graph
|
304
|
-
* @returns Observable that emits the current graph context
|
305
|
-
*/
|
306
|
-
observeState(): Observable<GraphContext<T>> {
|
307
|
-
return this.stateSubject.asObservable().pipe(
|
308
|
-
takeUntil(this.destroySubject),
|
309
|
-
distinctUntilChanged(
|
310
|
-
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
311
|
-
)
|
312
|
-
);
|
313
|
-
}
|
314
|
-
|
315
|
-
/**
|
316
|
-
* Observes specific event types in the graph
|
317
|
-
* @param eventType - The type of event to observe
|
318
|
-
* @returns Observable that emits events of the specified type
|
319
|
-
*/
|
320
|
-
observeEvents(eventType: string): Observable<GraphEvent<T>> {
|
321
|
-
return this.eventSubject.asObservable().pipe(
|
322
|
-
takeUntil(this.destroySubject),
|
323
|
-
filter((event) => event.type === eventType)
|
324
|
-
);
|
325
|
-
}
|
326
|
-
|
327
|
-
/**
|
328
|
-
* Observes state changes for a specific node
|
329
|
-
* @param nodeName - The name of the node to observe
|
330
|
-
* @returns Observable that emits the graph context when the specified node changes
|
331
|
-
*/
|
332
|
-
observeNodeState(nodeName: string): Observable<GraphContext<T>> {
|
333
|
-
return this.eventSubject.asObservable().pipe(
|
334
|
-
takeUntil(this.destroySubject),
|
335
|
-
filter(
|
336
|
-
(event) =>
|
337
|
-
event.type === "nodeStateChanged" && event.payload?.name === nodeName
|
338
|
-
),
|
339
|
-
map(() => this.stateSubject.value)
|
340
|
-
);
|
341
|
-
}
|
342
|
-
|
343
|
-
/**
|
344
|
-
* Streams a message letter by letter with a specified delay
|
345
|
-
* @param message - The message to stream
|
346
|
-
* @param delayMs - The delay in milliseconds between each letter
|
347
|
-
* @param property - The property name being streamed
|
348
|
-
* @returns An Observable that emits each letter of the message along with its property
|
349
|
-
*/
|
350
|
-
streamMessage(
|
351
|
-
message: string,
|
352
|
-
delayMs: number,
|
353
|
-
property: string
|
354
|
-
): Observable<{ letter: string; property: string }> {
|
355
|
-
return new Observable<{ letter: string; property: string }>(
|
356
|
-
(subscriber) => {
|
357
|
-
for (let i = 0; i < message.length; i++) {
|
358
|
-
setTimeout(() => {
|
359
|
-
subscriber.next({ letter: message[i], property });
|
360
|
-
if (i === message.length - 1) {
|
361
|
-
subscriber.complete();
|
362
|
-
}
|
363
|
-
}, i * delayMs);
|
364
|
-
}
|
365
|
-
}
|
366
|
-
);
|
367
|
-
}
|
368
|
-
}
|