@flowcore/pathways 0.8.0 → 0.9.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/CHANGELOG.md +19 -0
- package/README.md +95 -0
- package/esm/pathways/builder.d.ts +31 -1
- package/esm/pathways/builder.d.ts.map +1 -1
- package/esm/pathways/builder.js +47 -5
- package/esm/pathways/kv/kv-adapter.d.ts +59 -2
- package/esm/pathways/kv/kv-adapter.d.ts.map +1 -1
- package/esm/pathways/kv/kv-adapter.js +31 -1
- package/esm/pathways/postgres/postgres-pathway-state.d.ts +131 -1
- package/esm/pathways/postgres/postgres-pathway-state.d.ts.map +1 -1
- package/esm/pathways/postgres/postgres-pathway-state.js +131 -1
- package/esm/pathways/session-pathway.d.ts +63 -1
- package/esm/pathways/session-pathway.d.ts.map +1 -1
- package/esm/pathways/session-pathway.js +65 -0
- package/esm/router/index.d.ts +83 -2
- package/esm/router/index.d.ts.map +1 -1
- package/esm/router/index.js +83 -2
- package/package.json +4 -5
- package/script/pathways/builder.d.ts +31 -1
- package/script/pathways/builder.d.ts.map +1 -1
- package/script/pathways/builder.js +47 -5
- package/script/pathways/kv/kv-adapter.d.ts +59 -2
- package/script/pathways/kv/kv-adapter.d.ts.map +1 -1
- package/script/pathways/kv/kv-adapter.js +31 -1
- package/script/pathways/postgres/postgres-pathway-state.d.ts +131 -1
- package/script/pathways/postgres/postgres-pathway-state.d.ts.map +1 -1
- package/script/pathways/postgres/postgres-pathway-state.js +131 -1
- package/script/pathways/session-pathway.d.ts +63 -1
- package/script/pathways/session-pathway.d.ts.map +1 -1
- package/script/pathways/session-pathway.js +65 -0
- package/script/router/index.d.ts +83 -2
- package/script/router/index.d.ts.map +1 -1
- package/script/router/index.js +83 -2
|
@@ -4,6 +4,47 @@ import { createPostgresAdapter } from "./postgres-adapter.js";
|
|
|
4
4
|
*
|
|
5
5
|
* This class provides persistent storage of pathway state using a PostgreSQL database,
|
|
6
6
|
* which allows for state to be shared across multiple instances of the application.
|
|
7
|
+
*
|
|
8
|
+
* Key features:
|
|
9
|
+
* - Persistent storage of pathway processing state across application restarts
|
|
10
|
+
* - Automatic table and index creation
|
|
11
|
+
* - Configurable TTL (time-to-live) for processed events
|
|
12
|
+
* - Automatic cleanup of expired records
|
|
13
|
+
* - Support for horizontal scaling across multiple instances
|
|
14
|
+
* - Connection pooling for efficient database usage
|
|
15
|
+
*
|
|
16
|
+
* Use cases:
|
|
17
|
+
* - Production deployments that require durability and persistence
|
|
18
|
+
* - Distributed systems where multiple instances process events
|
|
19
|
+
* - Applications with high reliability requirements
|
|
20
|
+
* - Scenarios where in-memory state is insufficient
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // Create PostgreSQL pathway state with connection string
|
|
25
|
+
* const postgresState = createPostgresPathwayState({
|
|
26
|
+
* connectionString: "postgres://user:password@localhost:5432/mydb",
|
|
27
|
+
* tableName: "event_processing_state", // Optional
|
|
28
|
+
* ttlMs: 24 * 60 * 60 * 1000 // 24 hours (optional)
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Or with individual parameters
|
|
32
|
+
* const postgresState = createPostgresPathwayState({
|
|
33
|
+
* host: "localhost",
|
|
34
|
+
* port: 5432,
|
|
35
|
+
* user: "postgres",
|
|
36
|
+
* password: "postgres",
|
|
37
|
+
* database: "mydb",
|
|
38
|
+
* ssl: false,
|
|
39
|
+
* tableName: "event_processing_state", // Optional
|
|
40
|
+
* ttlMs: 30 * 60 * 1000 // 30 minutes (optional)
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Use with PathwaysBuilder
|
|
44
|
+
* const pathways = new PathwaysBuilder({
|
|
45
|
+
* // ... other config
|
|
46
|
+
* }).withPathwayState(postgresState);
|
|
47
|
+
* ```
|
|
7
48
|
*/
|
|
8
49
|
export class PostgresPathwayState {
|
|
9
50
|
/**
|
|
@@ -109,8 +150,30 @@ export class PostgresPathwayState {
|
|
|
109
150
|
/**
|
|
110
151
|
* Checks if an event has already been processed
|
|
111
152
|
*
|
|
153
|
+
* This method checks the PostgreSQL database to determine if an event with the given ID
|
|
154
|
+
* has been marked as processed. If the event exists in the database and is marked as processed,
|
|
155
|
+
* the method returns true.
|
|
156
|
+
*
|
|
157
|
+
* Before performing the check, this method also triggers cleanup of expired event records
|
|
158
|
+
* to maintain database performance.
|
|
159
|
+
*
|
|
112
160
|
* @param {string} eventId - The ID of the event to check
|
|
113
161
|
* @returns {Promise<boolean>} True if the event has been processed, false otherwise
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* // Check if an event has been processed
|
|
166
|
+
* const processed = await postgresState.isProcessed("event-123");
|
|
167
|
+
* if (processed) {
|
|
168
|
+
* console.log("Event has already been processed, skipping");
|
|
169
|
+
* } else {
|
|
170
|
+
* console.log("Processing event for the first time");
|
|
171
|
+
* // Process the event
|
|
172
|
+
* await processEvent(event);
|
|
173
|
+
* // Mark as processed
|
|
174
|
+
* await postgresState.setProcessed("event-123");
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
114
177
|
*/
|
|
115
178
|
async isProcessed(eventId) {
|
|
116
179
|
await this.initialize();
|
|
@@ -125,8 +188,37 @@ export class PostgresPathwayState {
|
|
|
125
188
|
/**
|
|
126
189
|
* Marks an event as processed
|
|
127
190
|
*
|
|
191
|
+
* This method inserts or updates a record in the PostgreSQL database to mark an event
|
|
192
|
+
* as processed. If the event already exists in the database, the record is updated;
|
|
193
|
+
* otherwise, a new record is created.
|
|
194
|
+
*
|
|
195
|
+
* Each processed event is stored with an expiration timestamp based on the configured TTL.
|
|
196
|
+
* After this time elapses, the record may be automatically removed during cleanup operations.
|
|
197
|
+
*
|
|
128
198
|
* @param {string} eventId - The ID of the event to mark as processed
|
|
129
199
|
* @returns {Promise<void>}
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* // Process an event and mark it as processed
|
|
204
|
+
* async function handleEvent(event) {
|
|
205
|
+
* // Check if already processed to implement idempotency
|
|
206
|
+
* if (await postgresState.isProcessed(event.id)) {
|
|
207
|
+
* return; // Skip already processed events
|
|
208
|
+
* }
|
|
209
|
+
*
|
|
210
|
+
* try {
|
|
211
|
+
* // Process the event
|
|
212
|
+
* await processEvent(event);
|
|
213
|
+
*
|
|
214
|
+
* // Mark as processed after successful processing
|
|
215
|
+
* await postgresState.setProcessed(event.id);
|
|
216
|
+
* } catch (error) {
|
|
217
|
+
* console.error("Failed to process event:", error);
|
|
218
|
+
* // Not marking as processed, so it can be retried
|
|
219
|
+
* }
|
|
220
|
+
* }
|
|
221
|
+
* ```
|
|
130
222
|
*/
|
|
131
223
|
async setProcessed(eventId) {
|
|
132
224
|
await this.initialize();
|
|
@@ -188,8 +280,46 @@ Object.defineProperty(PostgresPathwayState, "DEFAULT_TABLE_NAME", {
|
|
|
188
280
|
/**
|
|
189
281
|
* Creates a new PostgreSQL pathway state instance
|
|
190
282
|
*
|
|
191
|
-
*
|
|
283
|
+
* This is a factory function that simplifies the creation of PostgresPathwayState instances.
|
|
284
|
+
* It accepts either a connection string or individual connection parameters, along with
|
|
285
|
+
* optional configuration for table name and TTL.
|
|
286
|
+
*
|
|
287
|
+
* The PostgresPathwayState is lazily initialized, meaning the database connection and
|
|
288
|
+
* table creation only happen when the first operation is performed. This makes it safe
|
|
289
|
+
* to create instances early in the application lifecycle.
|
|
290
|
+
*
|
|
291
|
+
* @param config The PostgreSQL configuration (connection string or parameters)
|
|
192
292
|
* @returns A new PostgresPathwayState instance
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* // With connection string
|
|
297
|
+
* const state = createPostgresPathwayState({
|
|
298
|
+
* connectionString: "postgres://user:pass@localhost:5432/db?sslmode=require"
|
|
299
|
+
* });
|
|
300
|
+
*
|
|
301
|
+
* // With individual parameters
|
|
302
|
+
* const state = createPostgresPathwayState({
|
|
303
|
+
* host: "localhost",
|
|
304
|
+
* port: 5432,
|
|
305
|
+
* user: "postgres",
|
|
306
|
+
* password: "secret",
|
|
307
|
+
* database: "events_db",
|
|
308
|
+
* ssl: true
|
|
309
|
+
* });
|
|
310
|
+
*
|
|
311
|
+
* // With custom table name and TTL
|
|
312
|
+
* const state = createPostgresPathwayState({
|
|
313
|
+
* connectionString: "postgres://user:pass@localhost:5432/db",
|
|
314
|
+
* tableName: "my_custom_event_state",
|
|
315
|
+
* ttlMs: 7 * 24 * 60 * 60 * 1000 // 1 week
|
|
316
|
+
* });
|
|
317
|
+
*
|
|
318
|
+
* // Use with PathwaysBuilder
|
|
319
|
+
* const pathways = new PathwaysBuilder({
|
|
320
|
+
* // Other config
|
|
321
|
+
* }).withPathwayState(state);
|
|
322
|
+
* ```
|
|
193
323
|
*/
|
|
194
324
|
export function createPostgresPathwayState(config) {
|
|
195
325
|
const state = new PostgresPathwayState(config);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PathwaysBuilder } from "./builder.js";
|
|
1
|
+
import type { PathwaysBuilder, UserIdResolver } from "./builder.js";
|
|
2
2
|
import type { EventMetadata, PathwayWriteOptions } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* SessionPathwayBuilder wraps a PathwaysBuilder instance and automatically
|
|
@@ -6,6 +6,38 @@ import type { EventMetadata, PathwayWriteOptions } from "./types.js";
|
|
|
6
6
|
*
|
|
7
7
|
* This provides a convenient way to track operations within a user session
|
|
8
8
|
* by automatically including the session ID in metadata.
|
|
9
|
+
*
|
|
10
|
+
* Key features:
|
|
11
|
+
* - Automatic session ID generation if none is provided
|
|
12
|
+
* - Cross-platform UUID generation (works in Deno, Bun, and Node.js)
|
|
13
|
+
* - Simple API for accessing the current session ID
|
|
14
|
+
* - Convenient integration with session-specific user resolvers
|
|
15
|
+
* - Automatic inclusion of session ID in all write operations
|
|
16
|
+
* - Support for overriding the session ID on specific writes
|
|
17
|
+
*
|
|
18
|
+
* Use cases:
|
|
19
|
+
* - Tracking user actions across multiple pathway writes
|
|
20
|
+
* - Connecting related events in a single user session
|
|
21
|
+
* - Supporting multi-user environments where different users' operations need to be tracked separately
|
|
22
|
+
* - Building user activity logs with session grouping
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // Create a session pathway with auto-generated ID
|
|
27
|
+
* const session = new SessionPathwayBuilder(pathwaysBuilder);
|
|
28
|
+
*
|
|
29
|
+
* // Get the auto-generated session ID
|
|
30
|
+
* const sessionId = session.getSessionId();
|
|
31
|
+
*
|
|
32
|
+
* // Register a user resolver for this session
|
|
33
|
+
* session.withUserResolver(async () => getCurrentUserId());
|
|
34
|
+
*
|
|
35
|
+
* // Write events with session context
|
|
36
|
+
* await session.write("order/placed", orderData);
|
|
37
|
+
* await session.write("user/action", actionData);
|
|
38
|
+
*
|
|
39
|
+
* // All events will be associated with the same session ID
|
|
40
|
+
* ```
|
|
9
41
|
*/
|
|
10
42
|
export declare class SessionPathwayBuilder<TPathway extends Record<string, unknown> = {}, TWritablePaths extends keyof TPathway = never> {
|
|
11
43
|
private readonly pathwaysBuilder;
|
|
@@ -23,6 +55,36 @@ export declare class SessionPathwayBuilder<TPathway extends Record<string, unkno
|
|
|
23
55
|
* @returns The session ID associated with this instance
|
|
24
56
|
*/
|
|
25
57
|
getSessionId(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Registers a user resolver for this session
|
|
60
|
+
*
|
|
61
|
+
* This is a convenience method that calls `pathwaysBuilder.withSessionUserResolver`
|
|
62
|
+
* with the current session ID, allowing you to set up a resolver specific to this session
|
|
63
|
+
* without having to manually pass the session ID.
|
|
64
|
+
*
|
|
65
|
+
* The resolver will be called whenever events are written through this session,
|
|
66
|
+
* and the resolved user ID will be included in the event metadata.
|
|
67
|
+
*
|
|
68
|
+
* @param resolver The function that resolves to the user ID for this session
|
|
69
|
+
* @returns The SessionPathwayBuilder instance for chaining
|
|
70
|
+
*
|
|
71
|
+
* @throws Error if the underlying PathwaysBuilder does not have session user resolvers configured
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const session = new SessionPathwayBuilder(pathwaysBuilder);
|
|
76
|
+
*
|
|
77
|
+
* // Register a user resolver for this session
|
|
78
|
+
* session.withUserResolver(async () => {
|
|
79
|
+
* // Get the user ID associated with this session
|
|
80
|
+
* return getUserIdFromSession();
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* // When writing events, the user ID will be automatically included
|
|
84
|
+
* await session.write("user/action", actionData);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
withUserResolver(resolver: UserIdResolver): this;
|
|
26
88
|
/**
|
|
27
89
|
* Writes data to a pathway, proxying to the underlying PathwaysBuilder
|
|
28
90
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-pathway.d.ts","sourceRoot":"","sources":["../../src/pathways/session-pathway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"session-pathway.d.ts","sourceRoot":"","sources":["../../src/pathways/session-pathway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqBrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,qBAAqB,CAEhC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAC7C,cAAc,SAAS,MAAM,QAAQ,GAAG,KAAK;IAE7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4C;IAC5E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC;;;;;OAKG;gBAED,eAAe,EAAE,eAAe,CAAC,QAAQ,EAAE,cAAc,CAAC,EAC1D,SAAS,CAAC,EAAE,MAAM;IAMpB;;;;OAIG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAKhD;;;;;;;;OAQG;IACG,KAAK,CAAC,KAAK,SAAS,cAAc,EACtC,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EACrB,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;CAU9B"}
|
|
@@ -21,6 +21,38 @@ function generateUUID() {
|
|
|
21
21
|
*
|
|
22
22
|
* This provides a convenient way to track operations within a user session
|
|
23
23
|
* by automatically including the session ID in metadata.
|
|
24
|
+
*
|
|
25
|
+
* Key features:
|
|
26
|
+
* - Automatic session ID generation if none is provided
|
|
27
|
+
* - Cross-platform UUID generation (works in Deno, Bun, and Node.js)
|
|
28
|
+
* - Simple API for accessing the current session ID
|
|
29
|
+
* - Convenient integration with session-specific user resolvers
|
|
30
|
+
* - Automatic inclusion of session ID in all write operations
|
|
31
|
+
* - Support for overriding the session ID on specific writes
|
|
32
|
+
*
|
|
33
|
+
* Use cases:
|
|
34
|
+
* - Tracking user actions across multiple pathway writes
|
|
35
|
+
* - Connecting related events in a single user session
|
|
36
|
+
* - Supporting multi-user environments where different users' operations need to be tracked separately
|
|
37
|
+
* - Building user activity logs with session grouping
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Create a session pathway with auto-generated ID
|
|
42
|
+
* const session = new SessionPathwayBuilder(pathwaysBuilder);
|
|
43
|
+
*
|
|
44
|
+
* // Get the auto-generated session ID
|
|
45
|
+
* const sessionId = session.getSessionId();
|
|
46
|
+
*
|
|
47
|
+
* // Register a user resolver for this session
|
|
48
|
+
* session.withUserResolver(async () => getCurrentUserId());
|
|
49
|
+
*
|
|
50
|
+
* // Write events with session context
|
|
51
|
+
* await session.write("order/placed", orderData);
|
|
52
|
+
* await session.write("user/action", actionData);
|
|
53
|
+
*
|
|
54
|
+
* // All events will be associated with the same session ID
|
|
55
|
+
* ```
|
|
24
56
|
*/
|
|
25
57
|
export class SessionPathwayBuilder {
|
|
26
58
|
/**
|
|
@@ -53,6 +85,39 @@ export class SessionPathwayBuilder {
|
|
|
53
85
|
getSessionId() {
|
|
54
86
|
return this.sessionId;
|
|
55
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Registers a user resolver for this session
|
|
90
|
+
*
|
|
91
|
+
* This is a convenience method that calls `pathwaysBuilder.withSessionUserResolver`
|
|
92
|
+
* with the current session ID, allowing you to set up a resolver specific to this session
|
|
93
|
+
* without having to manually pass the session ID.
|
|
94
|
+
*
|
|
95
|
+
* The resolver will be called whenever events are written through this session,
|
|
96
|
+
* and the resolved user ID will be included in the event metadata.
|
|
97
|
+
*
|
|
98
|
+
* @param resolver The function that resolves to the user ID for this session
|
|
99
|
+
* @returns The SessionPathwayBuilder instance for chaining
|
|
100
|
+
*
|
|
101
|
+
* @throws Error if the underlying PathwaysBuilder does not have session user resolvers configured
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const session = new SessionPathwayBuilder(pathwaysBuilder);
|
|
106
|
+
*
|
|
107
|
+
* // Register a user resolver for this session
|
|
108
|
+
* session.withUserResolver(async () => {
|
|
109
|
+
* // Get the user ID associated with this session
|
|
110
|
+
* return getUserIdFromSession();
|
|
111
|
+
* });
|
|
112
|
+
*
|
|
113
|
+
* // When writing events, the user ID will be automatically included
|
|
114
|
+
* await session.write("user/action", actionData);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
withUserResolver(resolver) {
|
|
118
|
+
this.pathwaysBuilder.withSessionUserResolver(this.sessionId, resolver);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
56
121
|
/**
|
|
57
122
|
* Writes data to a pathway, proxying to the underlying PathwaysBuilder
|
|
58
123
|
*
|
package/esm/router/index.d.ts
CHANGED
|
@@ -11,6 +11,49 @@ import type { PathwaysBuilder } from "../pathways/index.js";
|
|
|
11
11
|
import type { Logger } from "../pathways/logger.js";
|
|
12
12
|
/**
|
|
13
13
|
* Router class that handles directing events to the appropriate pathway handlers
|
|
14
|
+
*
|
|
15
|
+
* The PathwayRouter serves as a bridge between incoming webhook events and the PathwaysBuilder,
|
|
16
|
+
* ensuring events are routed to the correct pathway handlers based on flow type and event type.
|
|
17
|
+
*
|
|
18
|
+
* Key features:
|
|
19
|
+
* - Secure authentication using a secret key
|
|
20
|
+
* - Automatic mapping of events to the correct pathway handlers
|
|
21
|
+
* - Compatibility with both legacy and modern Flowcore event formats
|
|
22
|
+
* - Detailed error handling and logging
|
|
23
|
+
*
|
|
24
|
+
* Use cases:
|
|
25
|
+
* - Building webhook endpoints that receive Flowcore events
|
|
26
|
+
* - Creating API routes that process events from external systems
|
|
27
|
+
* - Implementing event-driven microservices that consume Flowcore events
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // Create a router with authentication
|
|
32
|
+
* const SECRET_KEY = "your-webhook-secret";
|
|
33
|
+
* const router = new PathwayRouter(pathwaysBuilder, SECRET_KEY);
|
|
34
|
+
*
|
|
35
|
+
* // In your HTTP handler:
|
|
36
|
+
* async function handleWebhook(req: Request) {
|
|
37
|
+
* const event = await req.json();
|
|
38
|
+
* const secret = req.headers.get("X-Webhook-Secret");
|
|
39
|
+
*
|
|
40
|
+
* try {
|
|
41
|
+
* const result = await router.processEvent(event, secret);
|
|
42
|
+
* return new Response(JSON.stringify(result), {
|
|
43
|
+
* status: 200,
|
|
44
|
+
* headers: { "Content-Type": "application/json" }
|
|
45
|
+
* });
|
|
46
|
+
* } catch (error) {
|
|
47
|
+
* console.error("Error processing event:", error);
|
|
48
|
+
* return new Response(JSON.stringify({
|
|
49
|
+
* error: error.message
|
|
50
|
+
* }), {
|
|
51
|
+
* status: 401,
|
|
52
|
+
* headers: { "Content-Type": "application/json" }
|
|
53
|
+
* });
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
14
57
|
*/
|
|
15
58
|
export declare class PathwayRouter {
|
|
16
59
|
private readonly pathways;
|
|
@@ -28,10 +71,48 @@ export declare class PathwayRouter {
|
|
|
28
71
|
/**
|
|
29
72
|
* Process an incoming event by routing it to the appropriate pathway handler
|
|
30
73
|
*
|
|
31
|
-
*
|
|
74
|
+
* This method handles the complete lifecycle of an incoming event:
|
|
75
|
+
* 1. Validates the authentication using the provided secret key
|
|
76
|
+
* 2. Maps the event to the correct pathway based on flowType and eventType
|
|
77
|
+
* 3. Delegates processing to the PathwaysBuilder
|
|
78
|
+
* 4. Provides detailed error handling and feedback
|
|
79
|
+
*
|
|
80
|
+
* The method supports both modern Flowcore events and legacy events that used
|
|
81
|
+
* the "aggregator" field instead of "flowType". It automatically converts legacy
|
|
82
|
+
* events to the modern format before processing.
|
|
83
|
+
*
|
|
84
|
+
* @param event The event to process, containing flowType, eventType, and payload
|
|
32
85
|
* @param providedSecret The secret key provided for authentication
|
|
33
86
|
* @returns Result of the event processing with success status and message
|
|
34
|
-
*
|
|
87
|
+
*
|
|
88
|
+
* @throws Error if authentication fails (401 unauthorized)
|
|
89
|
+
* @throws Error if the pathway is not found (404 not found)
|
|
90
|
+
* @throws Error if processing fails (includes the original error message)
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Basic usage
|
|
95
|
+
* try {
|
|
96
|
+
* const result = await router.processEvent(incomingEvent, secretFromHeader);
|
|
97
|
+
* console.log("Success:", result.message);
|
|
98
|
+
* } catch (error) {
|
|
99
|
+
* console.error("Failed to process event:", error.message);
|
|
100
|
+
* }
|
|
101
|
+
*
|
|
102
|
+
* // With error handling for different error types
|
|
103
|
+
* try {
|
|
104
|
+
* const result = await router.processEvent(event, secret);
|
|
105
|
+
* return { status: 200, body: result };
|
|
106
|
+
* } catch (error) {
|
|
107
|
+
* if (error.message.includes("Invalid secret key")) {
|
|
108
|
+
* return { status: 401, body: { error: "Unauthorized" } };
|
|
109
|
+
* } else if (error.message.includes("not found")) {
|
|
110
|
+
* return { status: 404, body: { error: "Pathway not found" } };
|
|
111
|
+
* } else {
|
|
112
|
+
* return { status: 500, body: { error: "Processing failed" } };
|
|
113
|
+
* }
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
35
116
|
*/
|
|
36
117
|
processEvent(event: FlowcoreLegacyEvent, providedSecret: string): Promise<{
|
|
37
118
|
success: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAErE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAGnD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAErE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAGnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAb5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC;;;;;;;OAOG;gBAGgB,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAC9C,SAAS,EAAE,MAAM,EAClC,MAAM,CAAC,EAAE,MAAM;IAajB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACG,YAAY,CAAC,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAkDvH"}
|
package/esm/router/index.js
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
import { NoopLogger } from "../pathways/logger.js";
|
|
2
2
|
/**
|
|
3
3
|
* Router class that handles directing events to the appropriate pathway handlers
|
|
4
|
+
*
|
|
5
|
+
* The PathwayRouter serves as a bridge between incoming webhook events and the PathwaysBuilder,
|
|
6
|
+
* ensuring events are routed to the correct pathway handlers based on flow type and event type.
|
|
7
|
+
*
|
|
8
|
+
* Key features:
|
|
9
|
+
* - Secure authentication using a secret key
|
|
10
|
+
* - Automatic mapping of events to the correct pathway handlers
|
|
11
|
+
* - Compatibility with both legacy and modern Flowcore event formats
|
|
12
|
+
* - Detailed error handling and logging
|
|
13
|
+
*
|
|
14
|
+
* Use cases:
|
|
15
|
+
* - Building webhook endpoints that receive Flowcore events
|
|
16
|
+
* - Creating API routes that process events from external systems
|
|
17
|
+
* - Implementing event-driven microservices that consume Flowcore events
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // Create a router with authentication
|
|
22
|
+
* const SECRET_KEY = "your-webhook-secret";
|
|
23
|
+
* const router = new PathwayRouter(pathwaysBuilder, SECRET_KEY);
|
|
24
|
+
*
|
|
25
|
+
* // In your HTTP handler:
|
|
26
|
+
* async function handleWebhook(req: Request) {
|
|
27
|
+
* const event = await req.json();
|
|
28
|
+
* const secret = req.headers.get("X-Webhook-Secret");
|
|
29
|
+
*
|
|
30
|
+
* try {
|
|
31
|
+
* const result = await router.processEvent(event, secret);
|
|
32
|
+
* return new Response(JSON.stringify(result), {
|
|
33
|
+
* status: 200,
|
|
34
|
+
* headers: { "Content-Type": "application/json" }
|
|
35
|
+
* });
|
|
36
|
+
* } catch (error) {
|
|
37
|
+
* console.error("Error processing event:", error);
|
|
38
|
+
* return new Response(JSON.stringify({
|
|
39
|
+
* error: error.message
|
|
40
|
+
* }), {
|
|
41
|
+
* status: 401,
|
|
42
|
+
* headers: { "Content-Type": "application/json" }
|
|
43
|
+
* });
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
4
47
|
*/
|
|
5
48
|
export class PathwayRouter {
|
|
6
49
|
/**
|
|
@@ -43,10 +86,48 @@ export class PathwayRouter {
|
|
|
43
86
|
/**
|
|
44
87
|
* Process an incoming event by routing it to the appropriate pathway handler
|
|
45
88
|
*
|
|
46
|
-
*
|
|
89
|
+
* This method handles the complete lifecycle of an incoming event:
|
|
90
|
+
* 1. Validates the authentication using the provided secret key
|
|
91
|
+
* 2. Maps the event to the correct pathway based on flowType and eventType
|
|
92
|
+
* 3. Delegates processing to the PathwaysBuilder
|
|
93
|
+
* 4. Provides detailed error handling and feedback
|
|
94
|
+
*
|
|
95
|
+
* The method supports both modern Flowcore events and legacy events that used
|
|
96
|
+
* the "aggregator" field instead of "flowType". It automatically converts legacy
|
|
97
|
+
* events to the modern format before processing.
|
|
98
|
+
*
|
|
99
|
+
* @param event The event to process, containing flowType, eventType, and payload
|
|
47
100
|
* @param providedSecret The secret key provided for authentication
|
|
48
101
|
* @returns Result of the event processing with success status and message
|
|
49
|
-
*
|
|
102
|
+
*
|
|
103
|
+
* @throws Error if authentication fails (401 unauthorized)
|
|
104
|
+
* @throws Error if the pathway is not found (404 not found)
|
|
105
|
+
* @throws Error if processing fails (includes the original error message)
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // Basic usage
|
|
110
|
+
* try {
|
|
111
|
+
* const result = await router.processEvent(incomingEvent, secretFromHeader);
|
|
112
|
+
* console.log("Success:", result.message);
|
|
113
|
+
* } catch (error) {
|
|
114
|
+
* console.error("Failed to process event:", error.message);
|
|
115
|
+
* }
|
|
116
|
+
*
|
|
117
|
+
* // With error handling for different error types
|
|
118
|
+
* try {
|
|
119
|
+
* const result = await router.processEvent(event, secret);
|
|
120
|
+
* return { status: 200, body: result };
|
|
121
|
+
* } catch (error) {
|
|
122
|
+
* if (error.message.includes("Invalid secret key")) {
|
|
123
|
+
* return { status: 401, body: { error: "Unauthorized" } };
|
|
124
|
+
* } else if (error.message.includes("not found")) {
|
|
125
|
+
* return { status: 404, body: { error: "Pathway not found" } };
|
|
126
|
+
* } else {
|
|
127
|
+
* return { status: 500, body: { error: "Processing failed" } };
|
|
128
|
+
* }
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
50
131
|
*/
|
|
51
132
|
async processEvent(event, providedSecret) {
|
|
52
133
|
// Validate secret key
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowcore/pathways",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "A TypeScript Library for creating Flowcore Pathways, simplifying the integration with the flowcore platform",
|
|
5
|
-
"homepage": "https://github.com/flowcore-io/flowcore-
|
|
5
|
+
"homepage": "https://github.com/flowcore-io/flowcore-pathways#readme",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/flowcore-io/flowcore-
|
|
8
|
+
"url": "git+https://github.com/flowcore-io/flowcore-pathways.git"
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"bugs": {
|
|
12
|
-
"url": "https://github.com/flowcore-io/flowcore-
|
|
12
|
+
"url": "https://github.com/flowcore-io/flowcore-pathways/issues"
|
|
13
13
|
},
|
|
14
14
|
"main": "./script/mod.js",
|
|
15
15
|
"module": "./esm/mod.js",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@flowcore/sdk-transformer-core": "^2.3.6",
|
|
27
27
|
"@sinclair/typebox": "^0.34.27",
|
|
28
|
-
"bun-sqlite-key-value": "1.13.1",
|
|
29
28
|
"node-cache": "5.1.2",
|
|
30
29
|
"postgres": "^3.4.3",
|
|
31
30
|
"rxjs": "^7.8.1"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Static, TSchema } from "@sinclair/typebox";
|
|
2
2
|
import type { WebhookSendOptions } from "@flowcore/sdk-transformer-core";
|
|
3
3
|
import type { FlowcoreEvent } from "../contracts/event.js";
|
|
4
|
+
import type { KvAdapter } from "./kv/kv-adapter.js";
|
|
4
5
|
import type { Logger } from "./logger.js";
|
|
5
6
|
import type { EventMetadata, PathwayContract, PathwayKey, PathwayState, PathwayWriteOptions, WritablePathway } from "./types.js";
|
|
6
7
|
/**
|
|
@@ -77,14 +78,16 @@ export declare class PathwaysBuilder<TPathway extends Record<string, unknown> =
|
|
|
77
78
|
* @param options.apiKey The API key for authentication
|
|
78
79
|
* @param options.pathwayTimeoutMs Optional timeout for pathway processing in milliseconds
|
|
79
80
|
* @param options.logger Optional logger instance
|
|
81
|
+
* @param options.sessionUserResolvers Optional KvAdapter instance for session-specific user resolvers
|
|
80
82
|
*/
|
|
81
|
-
constructor({ baseUrl, tenant, dataCore, apiKey, pathwayTimeoutMs, logger, }: {
|
|
83
|
+
constructor({ baseUrl, tenant, dataCore, apiKey, pathwayTimeoutMs, logger, sessionUserResolvers, }: {
|
|
82
84
|
baseUrl: string;
|
|
83
85
|
tenant: string;
|
|
84
86
|
dataCore: string;
|
|
85
87
|
apiKey: string;
|
|
86
88
|
pathwayTimeoutMs?: number;
|
|
87
89
|
logger?: Logger;
|
|
90
|
+
sessionUserResolvers?: KvAdapter;
|
|
88
91
|
});
|
|
89
92
|
/**
|
|
90
93
|
* Configures the PathwaysBuilder to use a custom pathway state implementation
|
|
@@ -106,9 +109,36 @@ export declare class PathwaysBuilder<TPathway extends Record<string, unknown> =
|
|
|
106
109
|
withUserResolver(resolver: UserIdResolver): PathwaysBuilder<TPathway, TWritablePaths>;
|
|
107
110
|
/**
|
|
108
111
|
* Registers a user resolver for a specific session
|
|
112
|
+
*
|
|
113
|
+
* Session-specific user resolvers allow you to associate different user IDs with different
|
|
114
|
+
* sessions, which is useful in multi-user applications or when tracking user actions across
|
|
115
|
+
* different sessions.
|
|
116
|
+
*
|
|
117
|
+
* The resolver is stored in a key-value store with a TTL (time to live), and will be used
|
|
118
|
+
* to resolve the user ID when operations are performed with the given session ID. If the resolver
|
|
119
|
+
* expires, it will need to be registered again.
|
|
120
|
+
*
|
|
121
|
+
* This feature works in conjunction with the SessionPathwayBuilder to provide a complete
|
|
122
|
+
* session management solution.
|
|
123
|
+
*
|
|
109
124
|
* @param sessionId The session ID to associate with this resolver
|
|
110
125
|
* @param resolver The resolver function that resolves to the user ID for this session
|
|
111
126
|
* @returns The PathwaysBuilder instance for chaining
|
|
127
|
+
*
|
|
128
|
+
* @throws Error if session user resolvers are not configured (sessionUserResolvers not provided in constructor)
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // Register a resolver for a specific session
|
|
133
|
+
* pathwaysBuilder.withSessionUserResolver("session-123", async () => {
|
|
134
|
+
* return "user-456"; // Return the user ID for this session
|
|
135
|
+
* });
|
|
136
|
+
*
|
|
137
|
+
* // Use with SessionPathwayBuilder
|
|
138
|
+
* const session = new SessionPathwayBuilder(pathwaysBuilder, "session-123");
|
|
139
|
+
* await session.write("user/action", actionData);
|
|
140
|
+
* // The user ID will be automatically included in the metadata
|
|
141
|
+
* ```
|
|
112
142
|
*/
|
|
113
143
|
withSessionUserResolver(sessionId: string, resolver: UserIdResolver): PathwaysBuilder<TPathway, TWritablePaths>;
|
|
114
144
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/pathways/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,KAAK,EAAyD,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAG/H,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAA6B,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/pathways/builder.ts"],"names":[],"mappings":"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"}
|