@rotorsoft/act 0.6.28 → 0.6.29
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 -1
- package/dist/@types/act-builder.d.ts +286 -41
- package/dist/@types/act-builder.d.ts.map +1 -1
- package/dist/@types/act.d.ts +354 -52
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/InMemoryStore.d.ts +59 -8
- package/dist/@types/adapters/InMemoryStore.d.ts.map +1 -1
- package/dist/@types/config.d.ts +54 -6
- package/dist/@types/config.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +149 -10
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/state-builder.d.ts +318 -43
- package/dist/@types/state-builder.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +122 -10
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +211 -22
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +204 -28
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +107 -18
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/utils.d.ts +378 -27
- package/dist/@types/utils.d.ts.map +1 -1
- package/dist/index.cjs +352 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +352 -50
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/@types/act.d.ts
CHANGED
|
@@ -72,44 +72,179 @@ export declare class Act<S extends SchemaRegister<A>, E extends Schemas, A exten
|
|
|
72
72
|
*/
|
|
73
73
|
constructor(registry: Registry<S, E, A>);
|
|
74
74
|
/**
|
|
75
|
-
* Executes an action
|
|
75
|
+
* Executes an action on a state instance, committing resulting events.
|
|
76
76
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
77
|
+
* This is the primary method for modifying state. It:
|
|
78
|
+
* 1. Validates the action payload against the schema
|
|
79
|
+
* 2. Loads the current state snapshot
|
|
80
|
+
* 3. Checks invariants (business rules)
|
|
81
|
+
* 4. Executes the action handler to generate events
|
|
82
|
+
* 5. Applies events to create new state
|
|
83
|
+
* 6. Commits events to the store with optimistic concurrency control
|
|
84
84
|
*
|
|
85
|
-
* @
|
|
86
|
-
*
|
|
85
|
+
* @template K - Action name from registered actions
|
|
86
|
+
* @param action - The name of the action to execute
|
|
87
|
+
* @param target - Target specification with stream ID and actor context
|
|
88
|
+
* @param payload - Action payload matching the action's schema
|
|
89
|
+
* @param reactingTo - Optional event that triggered this action (for correlation)
|
|
90
|
+
* @param skipValidation - Skip schema validation (use carefully, for performance)
|
|
91
|
+
* @returns Array of snapshots for all affected states (usually one)
|
|
92
|
+
*
|
|
93
|
+
* @throws {ValidationError} If payload doesn't match action schema
|
|
94
|
+
* @throws {InvariantError} If business rules are violated
|
|
95
|
+
* @throws {ConcurrencyError} If another process modified the stream
|
|
96
|
+
*
|
|
97
|
+
* @example Basic action execution
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const snapshots = await app.do(
|
|
100
|
+
* "increment",
|
|
101
|
+
* {
|
|
102
|
+
* stream: "counter-1",
|
|
103
|
+
* actor: { id: "user1", name: "Alice" }
|
|
104
|
+
* },
|
|
105
|
+
* { by: 5 }
|
|
106
|
+
* );
|
|
107
|
+
*
|
|
108
|
+
* console.log(snapshots[0].state.count); // Current count after increment
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example With error handling
|
|
112
|
+
* ```typescript
|
|
113
|
+
* try {
|
|
114
|
+
* await app.do(
|
|
115
|
+
* "withdraw",
|
|
116
|
+
* { stream: "account-123", actor: { id: "user1", name: "Alice" } },
|
|
117
|
+
* { amount: 1000 }
|
|
118
|
+
* );
|
|
119
|
+
* } catch (error) {
|
|
120
|
+
* if (error instanceof InvariantError) {
|
|
121
|
+
* console.error("Business rule violated:", error.description);
|
|
122
|
+
* } else if (error instanceof ConcurrencyError) {
|
|
123
|
+
* console.error("Concurrent modification detected, retry...");
|
|
124
|
+
* } else if (error instanceof ValidationError) {
|
|
125
|
+
* console.error("Invalid payload:", error.details);
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Reaction triggering another action
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const app = act()
|
|
133
|
+
* .with(Order)
|
|
134
|
+
* .with(Inventory)
|
|
135
|
+
* .on("OrderPlaced")
|
|
136
|
+
* .do(async (event, context) => {
|
|
137
|
+
* // This action is triggered by an event
|
|
138
|
+
* const result = await context.app.do(
|
|
139
|
+
* "reduceStock",
|
|
140
|
+
* {
|
|
141
|
+
* stream: "inventory-1",
|
|
142
|
+
* actor: event.meta.causation.action.actor
|
|
143
|
+
* },
|
|
144
|
+
* { amount: event.data.items.length },
|
|
145
|
+
* event // Pass event for correlation tracking
|
|
146
|
+
* );
|
|
147
|
+
* return result;
|
|
148
|
+
* })
|
|
149
|
+
* .to("inventory-1")
|
|
150
|
+
* .build();
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @see {@link Target} for target structure
|
|
154
|
+
* @see {@link Snapshot} for return value structure
|
|
155
|
+
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
87
156
|
*/
|
|
88
157
|
do<K extends keyof A>(action: K, target: Target, payload: Readonly<A[K]>, reactingTo?: Committed<E, keyof E>, skipValidation?: boolean): Promise<Snapshot<S[K], E>[]>;
|
|
89
158
|
/**
|
|
90
|
-
* Loads the current state snapshot for a
|
|
159
|
+
* Loads the current state snapshot for a specific stream.
|
|
91
160
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @template AX The type of actions
|
|
95
|
-
* @param state The state machine definition
|
|
96
|
-
* @param stream The stream (instance) to load
|
|
97
|
-
* @param callback (Optional) Callback to receive the loaded snapshot
|
|
98
|
-
* @returns The snapshot of the loaded state
|
|
161
|
+
* Reconstructs the current state by replaying events from the event store.
|
|
162
|
+
* Uses snapshots when available to optimize loading performance.
|
|
99
163
|
*
|
|
100
|
-
* @
|
|
101
|
-
*
|
|
164
|
+
* @template SX - State schema type
|
|
165
|
+
* @template EX - Event schemas type
|
|
166
|
+
* @template AX - Action schemas type
|
|
167
|
+
* @param state - The state definition to load
|
|
168
|
+
* @param stream - The stream ID (state instance identifier)
|
|
169
|
+
* @param callback - Optional callback invoked with the loaded snapshot
|
|
170
|
+
* @returns The current state snapshot for the stream
|
|
171
|
+
*
|
|
172
|
+
* @example Load current state
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const snapshot = await app.load(Counter, "counter-1");
|
|
175
|
+
* console.log(snapshot.state.count); // Current count
|
|
176
|
+
* console.log(snapshot.version); // Number of events applied
|
|
177
|
+
* console.log(snapshot.patches); // Events since last snapshot
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @example With callback
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const snapshot = await app.load(User, "user-123", (snap) => {
|
|
183
|
+
* console.log("Loaded user:", snap.state.name);
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* @example Load multiple states
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const [user, account] = await Promise.all([
|
|
190
|
+
* app.load(User, "user-123"),
|
|
191
|
+
* app.load(BankAccount, "account-456")
|
|
192
|
+
* ]);
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @see {@link Snapshot} for snapshot structure
|
|
102
196
|
*/
|
|
103
197
|
load<SX extends Schema, EX extends Schemas, AX extends Schemas>(state: State<SX, EX, AX>, stream: string, callback?: (snapshot: Snapshot<SX, EX>) => void): Promise<Snapshot<SX, EX>>;
|
|
104
198
|
/**
|
|
105
|
-
*
|
|
199
|
+
* Queries the event store for events matching a filter.
|
|
106
200
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
201
|
+
* Use this for analyzing event streams, generating reports, or debugging.
|
|
202
|
+
* The callback is invoked for each matching event, and the method returns
|
|
203
|
+
* summary information (first event, last event, total count).
|
|
110
204
|
*
|
|
111
|
-
* @
|
|
112
|
-
*
|
|
205
|
+
* For small result sets, consider using {@link query_array} instead.
|
|
206
|
+
*
|
|
207
|
+
* @param query - The query filter
|
|
208
|
+
* @param query.stream - Filter by stream ID
|
|
209
|
+
* @param query.name - Filter by event name
|
|
210
|
+
* @param query.after - Filter events after this event ID
|
|
211
|
+
* @param query.before - Filter events before this event ID
|
|
212
|
+
* @param query.created_after - Filter events after this timestamp
|
|
213
|
+
* @param query.created_before - Filter events before this timestamp
|
|
214
|
+
* @param query.limit - Maximum number of events to return
|
|
215
|
+
* @param callback - Optional callback invoked for each matching event
|
|
216
|
+
* @returns Object with first event, last event, and total count
|
|
217
|
+
*
|
|
218
|
+
* @example Query all events for a stream
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const { first, last, count } = await app.query(
|
|
221
|
+
* { stream: "counter-1" },
|
|
222
|
+
* (event) => console.log(event.name, event.data)
|
|
223
|
+
* );
|
|
224
|
+
* console.log(`Found ${count} events from ${first?.id} to ${last?.id}`);
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* @example Query specific event types
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const { count } = await app.query(
|
|
230
|
+
* { name: "UserCreated", limit: 100 },
|
|
231
|
+
* (event) => {
|
|
232
|
+
* console.log("User created:", event.data.email);
|
|
233
|
+
* }
|
|
234
|
+
* );
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example Query events in time range
|
|
238
|
+
* ```typescript
|
|
239
|
+
* const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
240
|
+
* const { count } = await app.query({
|
|
241
|
+
* created_after: yesterday,
|
|
242
|
+
* stream: "user-123"
|
|
243
|
+
* });
|
|
244
|
+
* console.log(`User had ${count} events in last 24 hours`);
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @see {@link query_array} for loading events into memory
|
|
113
248
|
*/
|
|
114
249
|
query(query: Query, callback?: (event: Committed<E, keyof E>) => void): Promise<{
|
|
115
250
|
first?: Committed<E, keyof E>;
|
|
@@ -117,14 +252,30 @@ export declare class Act<S extends SchemaRegister<A>, E extends Schemas, A exten
|
|
|
117
252
|
count: number;
|
|
118
253
|
}>;
|
|
119
254
|
/**
|
|
120
|
-
*
|
|
121
|
-
* Use this version with caution, as it return events in memory.
|
|
255
|
+
* Queries the event store and returns all matching events in memory.
|
|
122
256
|
*
|
|
123
|
-
*
|
|
124
|
-
* @
|
|
257
|
+
* **Use with caution** - this loads all results into memory. For large result sets,
|
|
258
|
+
* use {@link query} with a callback instead to process events incrementally.
|
|
125
259
|
*
|
|
126
|
-
* @
|
|
127
|
-
*
|
|
260
|
+
* @param query - The query filter (same as {@link query})
|
|
261
|
+
* @returns Array of all matching events
|
|
262
|
+
*
|
|
263
|
+
* @example Load all events for a stream
|
|
264
|
+
* ```typescript
|
|
265
|
+
* const events = await app.query_array({ stream: "counter-1" });
|
|
266
|
+
* console.log(`Loaded ${events.length} events`);
|
|
267
|
+
* events.forEach(event => console.log(event.name, event.data));
|
|
268
|
+
* ```
|
|
269
|
+
*
|
|
270
|
+
* @example Get recent events
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const recent = await app.query_array({
|
|
273
|
+
* stream: "user-123",
|
|
274
|
+
* limit: 10
|
|
275
|
+
* });
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @see {@link query} for large result sets
|
|
128
279
|
*/
|
|
129
280
|
query_array(query: Query): Promise<Committed<E, keyof E>[]>;
|
|
130
281
|
/**
|
|
@@ -140,41 +291,192 @@ export declare class Act<S extends SchemaRegister<A>, E extends Schemas, A exten
|
|
|
140
291
|
*/
|
|
141
292
|
private handle;
|
|
142
293
|
/**
|
|
143
|
-
*
|
|
294
|
+
* Processes pending reactions by draining uncommitted events from the event store.
|
|
144
295
|
*
|
|
145
|
-
*
|
|
296
|
+
* The drain process:
|
|
297
|
+
* 1. Polls the store for streams with uncommitted events
|
|
298
|
+
* 2. Leases streams to prevent concurrent processing
|
|
299
|
+
* 3. Fetches events for each leased stream
|
|
300
|
+
* 4. Executes matching reaction handlers
|
|
301
|
+
* 5. Acknowledges successful reactions or blocks failing ones
|
|
146
302
|
*
|
|
147
|
-
*
|
|
303
|
+
* Drain uses a dual-frontier strategy to balance processing of new streams (lagging)
|
|
304
|
+
* vs active streams (leading). The ratio adapts based on event pressure.
|
|
148
305
|
*
|
|
149
|
-
*
|
|
306
|
+
* Call this method periodically in a background loop, or after committing events.
|
|
307
|
+
*
|
|
308
|
+
* @param options - Drain configuration options
|
|
309
|
+
* @param options.streamLimit - Maximum number of streams to process per cycle (default: 10)
|
|
310
|
+
* @param options.eventLimit - Maximum events to fetch per stream (default: 10)
|
|
311
|
+
* @param options.leaseMillis - Lease duration in milliseconds (default: 10000)
|
|
312
|
+
* @returns Drain statistics with fetched, leased, acked, and blocked counts
|
|
313
|
+
*
|
|
314
|
+
* @example Basic drain loop
|
|
315
|
+
* ```typescript
|
|
316
|
+
* // Process reactions after each action
|
|
317
|
+
* await app.do("createUser", target, payload);
|
|
150
318
|
* await app.drain();
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* @example Background drain worker
|
|
322
|
+
* ```typescript
|
|
323
|
+
* setInterval(async () => {
|
|
324
|
+
* try {
|
|
325
|
+
* const result = await app.drain({
|
|
326
|
+
* streamLimit: 20,
|
|
327
|
+
* eventLimit: 50
|
|
328
|
+
* });
|
|
329
|
+
* if (result.acked.length) {
|
|
330
|
+
* console.log(`Processed ${result.acked.length} streams`);
|
|
331
|
+
* }
|
|
332
|
+
* } catch (error) {
|
|
333
|
+
* console.error("Drain error:", error);
|
|
334
|
+
* }
|
|
335
|
+
* }, 5000); // Every 5 seconds
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* @example With lifecycle listeners
|
|
339
|
+
* ```typescript
|
|
340
|
+
* app.on("acked", (leases) => {
|
|
341
|
+
* console.log(`Acknowledged ${leases.length} streams`);
|
|
342
|
+
* });
|
|
343
|
+
*
|
|
344
|
+
* app.on("blocked", (blocked) => {
|
|
345
|
+
* console.error(`Blocked ${blocked.length} streams due to errors`);
|
|
346
|
+
* blocked.forEach(({ stream, error }) => {
|
|
347
|
+
* console.error(`Stream ${stream}: ${error}`);
|
|
348
|
+
* });
|
|
349
|
+
* });
|
|
350
|
+
*
|
|
351
|
+
* await app.drain();
|
|
352
|
+
* ```
|
|
353
|
+
*
|
|
354
|
+
* @see {@link correlate} for dynamic stream discovery
|
|
355
|
+
* @see {@link start_correlations} for automatic correlation
|
|
151
356
|
*/
|
|
152
357
|
drain<E extends Schemas>({ streamLimit, eventLimit, leaseMillis, }?: DrainOptions): Promise<Drain<E>>;
|
|
153
358
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
359
|
+
* Discovers and registers new streams dynamically based on reaction resolvers.
|
|
360
|
+
*
|
|
361
|
+
* Correlation enables "dynamic reactions" where target streams are determined at runtime
|
|
362
|
+
* based on event content. For example, you might create a stats stream for each user
|
|
363
|
+
* when they perform certain actions.
|
|
364
|
+
*
|
|
365
|
+
* This method scans events matching the query and identifies new target streams based
|
|
366
|
+
* on reaction resolvers. It then registers these streams so they'll be picked up by
|
|
367
|
+
* the next drain cycle.
|
|
368
|
+
*
|
|
369
|
+
* @param query - Query filter to scan for new correlations
|
|
370
|
+
* @param query.after - Start scanning after this event ID (default: -1)
|
|
371
|
+
* @param query.limit - Maximum events to scan (default: 10)
|
|
372
|
+
* @returns Object with newly leased streams and last scanned event ID
|
|
373
|
+
*
|
|
374
|
+
* @example Manual correlation
|
|
375
|
+
* ```typescript
|
|
376
|
+
* // Scan for new streams
|
|
377
|
+
* const { leased, last_id } = await app.correlate({ after: 0, limit: 100 });
|
|
378
|
+
* console.log(`Found ${leased.length} new streams`);
|
|
379
|
+
*
|
|
380
|
+
* // Save last_id for next scan
|
|
381
|
+
* await saveCheckpoint(last_id);
|
|
382
|
+
* ```
|
|
383
|
+
*
|
|
384
|
+
* @example Dynamic stream creation
|
|
385
|
+
* ```typescript
|
|
386
|
+
* const app = act()
|
|
387
|
+
* .with(User)
|
|
388
|
+
* .with(UserStats)
|
|
389
|
+
* .on("UserLoggedIn")
|
|
390
|
+
* .do(async (event) => ["incrementLoginCount", {}])
|
|
391
|
+
* .to((event) => ({
|
|
392
|
+
* target: `stats-${event.stream}` // Dynamic target per user
|
|
393
|
+
* }))
|
|
394
|
+
* .build();
|
|
395
|
+
*
|
|
396
|
+
* // Discover stats streams as users log in
|
|
397
|
+
* await app.correlate();
|
|
398
|
+
* ```
|
|
399
|
+
*
|
|
400
|
+
* @see {@link start_correlations} for automatic periodic correlation
|
|
401
|
+
* @see {@link stop_correlations} to stop automatic correlation
|
|
157
402
|
*/
|
|
158
403
|
correlate(query?: Query): Promise<{
|
|
159
404
|
leased: Lease[];
|
|
160
405
|
last_id: number;
|
|
161
406
|
}>;
|
|
162
407
|
/**
|
|
163
|
-
* Starts correlation worker
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* -
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* @param
|
|
175
|
-
* @
|
|
408
|
+
* Starts automatic periodic correlation worker for discovering new streams.
|
|
409
|
+
*
|
|
410
|
+
* The correlation worker runs in the background, scanning for new events and identifying
|
|
411
|
+
* new target streams based on reaction resolvers. It maintains a sliding window that
|
|
412
|
+
* advances with each scan, ensuring all events are eventually correlated.
|
|
413
|
+
*
|
|
414
|
+
* This is useful for dynamic stream creation patterns where you don't know all streams
|
|
415
|
+
* upfront - they're discovered as events arrive.
|
|
416
|
+
*
|
|
417
|
+
* **Note:** Only one correlation worker can run at a time per Act instance.
|
|
418
|
+
*
|
|
419
|
+
* @param query - Query filter for correlation scans
|
|
420
|
+
* @param query.after - Initial starting point (default: -1, start from beginning)
|
|
421
|
+
* @param query.limit - Events to scan per cycle (default: 100)
|
|
422
|
+
* @param frequency - Correlation frequency in milliseconds (default: 10000)
|
|
423
|
+
* @param callback - Optional callback invoked with newly discovered streams
|
|
424
|
+
* @returns `true` if worker started, `false` if already running
|
|
425
|
+
*
|
|
426
|
+
* @example Start automatic correlation
|
|
427
|
+
* ```typescript
|
|
428
|
+
* // Start correlation worker scanning every 5 seconds
|
|
429
|
+
* app.start_correlations(
|
|
430
|
+
* { after: 0, limit: 100 },
|
|
431
|
+
* 5000,
|
|
432
|
+
* (leased) => {
|
|
433
|
+
* console.log(`Discovered ${leased.length} new streams`);
|
|
434
|
+
* }
|
|
435
|
+
* );
|
|
436
|
+
*
|
|
437
|
+
* // Later, stop it
|
|
438
|
+
* app.stop_correlations();
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* @example With checkpoint persistence
|
|
442
|
+
* ```typescript
|
|
443
|
+
* // Load last checkpoint
|
|
444
|
+
* const lastId = await loadCheckpoint();
|
|
445
|
+
*
|
|
446
|
+
* app.start_correlations(
|
|
447
|
+
* { after: lastId, limit: 100 },
|
|
448
|
+
* 10000,
|
|
449
|
+
* async (leased) => {
|
|
450
|
+
* // Save checkpoint for next restart
|
|
451
|
+
* if (leased.length) {
|
|
452
|
+
* const maxId = Math.max(...leased.map(l => l.at));
|
|
453
|
+
* await saveCheckpoint(maxId);
|
|
454
|
+
* }
|
|
455
|
+
* }
|
|
456
|
+
* );
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* @see {@link correlate} for manual one-time correlation
|
|
460
|
+
* @see {@link stop_correlations} to stop the worker
|
|
176
461
|
*/
|
|
177
462
|
start_correlations(query?: Query, frequency?: number, callback?: (leased: Lease[]) => void): boolean;
|
|
463
|
+
/**
|
|
464
|
+
* Stops the automatic correlation worker.
|
|
465
|
+
*
|
|
466
|
+
* Call this to stop the background correlation worker started by {@link start_correlations}.
|
|
467
|
+
* This is automatically called when the Act instance is disposed.
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* ```typescript
|
|
471
|
+
* // Start correlation
|
|
472
|
+
* app.start_correlations();
|
|
473
|
+
*
|
|
474
|
+
* // Later, stop it
|
|
475
|
+
* app.stop_correlations();
|
|
476
|
+
* ```
|
|
477
|
+
*
|
|
478
|
+
* @see {@link start_correlations}
|
|
479
|
+
*/
|
|
178
480
|
stop_correlations(): void;
|
|
179
481
|
}
|
|
180
482
|
//# sourceMappingURL=act.d.ts.map
|
package/dist/@types/act.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/act.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EACL,YAAY,EACZ,KAAK,EACL,KAAK,EAEL,QAAQ,EACR,MAAM,EACN,cAAc,EACd,OAAO,EACP,QAAQ,EACR,KAAK,EACL,MAAM,EACP,MAAM,kBAAkB,CAAC;AAI1B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,GAAG,CACd,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,EAC3B,CAAC,SAAS,OAAO,EACjB,CAAC,SAAS,OAAO;aA8DW,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IA5DvD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,qBAAqB,CAAO;IACpC,OAAO,CAAC,qBAAqB,CAAyC;IAEtE;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO;IACzD,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO;IAC5C,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO;IAKvE;;;;;;OAMG;IACH,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,IAAI;IACxE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,IAAI;IAC3D,EAAE,CACA,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,GACzD,IAAI;IAMP;;;;;;OAMG;IACH,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,IAAI;IACzE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,IAAI;IAC5D,GAAG,CACD,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,GACzD,IAAI;IAMP;;;;OAIG;gBACyB,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAQvD
|
|
1
|
+
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/act.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,67 @@
|
|
|
1
1
|
import type { Committed, EventMeta, Lease, Message, Query, Schemas, Store } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* In-memory event store implementation.
|
|
4
|
+
*
|
|
5
|
+
* This is the default store used by Act when no other store is injected.
|
|
6
|
+
* It stores all events in memory and is suitable for:
|
|
7
|
+
* - Development and prototyping
|
|
8
|
+
* - Unit and integration testing
|
|
9
|
+
* - Demonstrations and examples
|
|
10
|
+
*
|
|
11
|
+
* **Not suitable for production** - all data is lost when the process exits.
|
|
12
|
+
* Use {@link PostgresStore} for production deployments.
|
|
13
|
+
*
|
|
14
|
+
* The in-memory store provides:
|
|
15
|
+
* - Full {@link Store} interface implementation
|
|
16
|
+
* - Optimistic concurrency control
|
|
17
|
+
* - Stream leasing for distributed processing simulation
|
|
18
|
+
* - Snapshot support
|
|
19
|
+
* - Fast performance (no I/O overhead)
|
|
20
|
+
*
|
|
21
|
+
* @example Using in tests
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { store } from "@rotorsoft/act";
|
|
24
|
+
*
|
|
25
|
+
* describe("Counter", () => {
|
|
26
|
+
* beforeEach(async () => {
|
|
27
|
+
* // Reset store between tests
|
|
28
|
+
* await store().seed();
|
|
29
|
+
* });
|
|
5
30
|
*
|
|
6
|
-
*
|
|
31
|
+
* it("increments", async () => {
|
|
32
|
+
* await app.do("increment", target, { by: 5 });
|
|
33
|
+
* const snapshot = await app.load(Counter, "counter-1");
|
|
34
|
+
* expect(snapshot.state.count).toBe(5);
|
|
35
|
+
* });
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
7
38
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
39
|
+
* @example Explicit instantiation
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { InMemoryStore } from "@rotorsoft/act";
|
|
10
42
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
43
|
+
* const testStore = new InMemoryStore();
|
|
44
|
+
* await testStore.seed();
|
|
45
|
+
*
|
|
46
|
+
* // Use for specific test scenarios
|
|
47
|
+
* await testStore.commit("test-stream", events, meta);
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @example Querying events
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const events: any[] = [];
|
|
53
|
+
* await store().query(
|
|
54
|
+
* (event) => events.push(event),
|
|
55
|
+
* { stream: "test-stream" }
|
|
56
|
+
* );
|
|
57
|
+
* console.log(`Found ${events.length} events`);
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @see {@link Store} for the interface definition
|
|
61
|
+
* @see {@link PostgresStore} for production use
|
|
62
|
+
* @see {@link store} for injecting stores
|
|
63
|
+
*
|
|
64
|
+
* @category Adapters
|
|
14
65
|
*/
|
|
15
66
|
export declare class InMemoryStore implements Store {
|
|
16
67
|
private _events;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryStore.d.ts","sourceRoot":"","sources":["../../../src/adapters/InMemoryStore.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,KAAK,EACL,OAAO,EACP,KAAK,EACL,OAAO,EACP,KAAK,EACN,MAAM,mBAAmB,CAAC;AAkG3B
|
|
1
|
+
{"version":3,"file":"InMemoryStore.d.ts","sourceRoot":"","sources":["../../../src/adapters/InMemoryStore.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,KAAK,EACL,OAAO,EACP,KAAK,EACL,OAAO,EACP,KAAK,EACN,MAAM,mBAAmB,CAAC;AAkG3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,qBAAa,aAAc,YAAW,KAAK;IAEzC,OAAO,CAAC,OAAO,CAA2C;IAE1D,OAAO,CAAC,QAAQ,CAA0C;IAE1D;;;OAGG;IACG,OAAO;IAKb;;;OAGG;IACG,IAAI;IAIV;;;OAGG;IACG,IAAI;IAMV,OAAO,CAAC,QAAQ;IAUhB;;;;;OAKG;IACG,KAAK,CAAC,CAAC,SAAS,OAAO,EAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,EAChD,KAAK,CAAC,EAAE,KAAK;IAiCf;;;;;;;;OAQG;IACG,MAAM,CAAC,CAAC,SAAS,OAAO,EAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAC3B,IAAI,EAAE,SAAS,EACf,eAAe,CAAC,EAAE,MAAM;IAiC1B;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;IAyB3C;;;;;OAKG;IACG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM;IAa3C;;;OAGG;IACG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE;;;;;;;;IAOzB;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;;;;;;;;;CAMrD"}
|
package/dist/@types/config.d.ts
CHANGED
|
@@ -55,15 +55,63 @@ declare const BaseSchema: z.ZodObject<{
|
|
|
55
55
|
*/
|
|
56
56
|
export type Config = z.infer<typeof BaseSchema>;
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Gets the current Act Framework configuration.
|
|
59
|
+
*
|
|
60
|
+
* Configuration is loaded from package.json and environment variables, providing
|
|
61
|
+
* type-safe access to application metadata and runtime settings.
|
|
62
|
+
*
|
|
63
|
+
* **Environment Variables:**
|
|
64
|
+
* - `NODE_ENV`: "development" | "test" | "staging" | "production" (default: "development")
|
|
65
|
+
* - `LOG_LEVEL`: "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
|
66
|
+
* - `LOG_SINGLE_LINE`: "true" | "false" (default: "true")
|
|
67
|
+
* - `SLEEP_MS`: Milliseconds for sleep utility (default: 100, 0 for tests)
|
|
68
|
+
*
|
|
69
|
+
* **Defaults by environment:**
|
|
70
|
+
* - test: logLevel="error", sleepMs=0
|
|
71
|
+
* - production: logLevel="info"
|
|
72
|
+
* - development: logLevel="trace"
|
|
73
|
+
*
|
|
74
|
+
* @returns The validated configuration object
|
|
75
|
+
*
|
|
76
|
+
* @example Basic usage
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { config } from "@rotorsoft/act";
|
|
59
79
|
*
|
|
60
|
-
* Merges package.json metadata with environment, logging, and timing options.
|
|
61
|
-
* @returns The validated configuration object.
|
|
62
|
-
* @example
|
|
63
|
-
* ```ts
|
|
64
80
|
* const cfg = config();
|
|
65
|
-
* console.log(cfg.
|
|
81
|
+
* console.log(`App: ${cfg.name} v${cfg.version}`);
|
|
82
|
+
* console.log(`Environment: ${cfg.env}`);
|
|
83
|
+
* console.log(`Log level: ${cfg.logLevel}`);
|
|
66
84
|
* ```
|
|
85
|
+
*
|
|
86
|
+
* @example Environment-specific behavior
|
|
87
|
+
* ```typescript
|
|
88
|
+
* import { config } from "@rotorsoft/act";
|
|
89
|
+
*
|
|
90
|
+
* const cfg = config();
|
|
91
|
+
*
|
|
92
|
+
* if (cfg.env === "production") {
|
|
93
|
+
* // Use PostgreSQL in production
|
|
94
|
+
* store(new PostgresStore(prodConfig));
|
|
95
|
+
* } else {
|
|
96
|
+
* // Use in-memory store for dev/test
|
|
97
|
+
* store(new InMemoryStore());
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example Adjusting log levels
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Set via environment variable:
|
|
104
|
+
* // LOG_LEVEL=debug npm start
|
|
105
|
+
*
|
|
106
|
+
* // Or check in code:
|
|
107
|
+
* const cfg = config();
|
|
108
|
+
* if (cfg.logLevel === "trace") {
|
|
109
|
+
* logger.trace("Detailed debugging enabled");
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @see {@link Config} for configuration type
|
|
114
|
+
* @see {@link Package} for package.json metadata
|
|
67
115
|
*/
|
|
68
116
|
export declare const config: () => Config;
|
|
69
117
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;iBAWxB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAYpD;;;;GAIG;AACH,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKd,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAgBhD
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;iBAWxB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAYpD;;;;GAIG;AACH,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKd,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAgBhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,eAAO,MAAM,MAAM,QAAO,MAEzB,CAAC"}
|