@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.
Files changed (33) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +95 -0
  3. package/esm/pathways/builder.d.ts +31 -1
  4. package/esm/pathways/builder.d.ts.map +1 -1
  5. package/esm/pathways/builder.js +47 -5
  6. package/esm/pathways/kv/kv-adapter.d.ts +59 -2
  7. package/esm/pathways/kv/kv-adapter.d.ts.map +1 -1
  8. package/esm/pathways/kv/kv-adapter.js +31 -1
  9. package/esm/pathways/postgres/postgres-pathway-state.d.ts +131 -1
  10. package/esm/pathways/postgres/postgres-pathway-state.d.ts.map +1 -1
  11. package/esm/pathways/postgres/postgres-pathway-state.js +131 -1
  12. package/esm/pathways/session-pathway.d.ts +63 -1
  13. package/esm/pathways/session-pathway.d.ts.map +1 -1
  14. package/esm/pathways/session-pathway.js +65 -0
  15. package/esm/router/index.d.ts +83 -2
  16. package/esm/router/index.d.ts.map +1 -1
  17. package/esm/router/index.js +83 -2
  18. package/package.json +4 -5
  19. package/script/pathways/builder.d.ts +31 -1
  20. package/script/pathways/builder.d.ts.map +1 -1
  21. package/script/pathways/builder.js +47 -5
  22. package/script/pathways/kv/kv-adapter.d.ts +59 -2
  23. package/script/pathways/kv/kv-adapter.d.ts.map +1 -1
  24. package/script/pathways/kv/kv-adapter.js +31 -1
  25. package/script/pathways/postgres/postgres-pathway-state.d.ts +131 -1
  26. package/script/pathways/postgres/postgres-pathway-state.d.ts.map +1 -1
  27. package/script/pathways/postgres/postgres-pathway-state.js +131 -1
  28. package/script/pathways/session-pathway.d.ts +63 -1
  29. package/script/pathways/session-pathway.d.ts.map +1 -1
  30. package/script/pathways/session-pathway.js +65 -0
  31. package/script/router/index.d.ts +83 -2
  32. package/script/router/index.d.ts.map +1 -1
  33. package/script/router/index.js +83 -2
@@ -18,6 +18,10 @@ const DEFAULT_MAX_RETRIES = 3;
18
18
  * Default delay between retry attempts in milliseconds
19
19
  */
20
20
  const DEFAULT_RETRY_DELAY_MS = 500;
21
+ /**
22
+ * Default TTL for session-specific user resolvers in milliseconds (10seconds)
23
+ */
24
+ const DEFAULT_SESSION_USER_RESOLVER_TTL_MS = 10 * 1000;
21
25
  /**
22
26
  * Main builder class for creating and managing Flowcore pathways
23
27
  *
@@ -42,8 +46,9 @@ class PathwaysBuilder {
42
46
  * @param options.apiKey The API key for authentication
43
47
  * @param options.pathwayTimeoutMs Optional timeout for pathway processing in milliseconds
44
48
  * @param options.logger Optional logger instance
49
+ * @param options.sessionUserResolvers Optional KvAdapter instance for session-specific user resolvers
45
50
  */
46
- constructor({ baseUrl, tenant, dataCore, apiKey, pathwayTimeoutMs, logger, }) {
51
+ constructor({ baseUrl, tenant, dataCore, apiKey, pathwayTimeoutMs, logger, sessionUserResolvers, }) {
47
52
  Object.defineProperty(this, "pathways", {
48
53
  enumerable: true,
49
54
  configurable: true,
@@ -158,7 +163,7 @@ class PathwaysBuilder {
158
163
  enumerable: true,
159
164
  configurable: true,
160
165
  writable: true,
161
- value: new Map()
166
+ value: null
162
167
  });
163
168
  // Logger instance (but not using it yet due to TypeScript errors)
164
169
  Object.defineProperty(this, "logger", {
@@ -199,6 +204,9 @@ class PathwaysBuilder {
199
204
  this.tenant = tenant;
200
205
  this.dataCore = dataCore;
201
206
  this.apiKey = apiKey;
207
+ if (sessionUserResolvers) {
208
+ this.sessionUserResolvers = sessionUserResolvers;
209
+ }
202
210
  this.logger.debug('Initializing PathwaysBuilder', {
203
211
  baseUrl,
204
212
  tenant,
@@ -252,13 +260,43 @@ class PathwaysBuilder {
252
260
  }
253
261
  /**
254
262
  * Registers a user resolver for a specific session
263
+ *
264
+ * Session-specific user resolvers allow you to associate different user IDs with different
265
+ * sessions, which is useful in multi-user applications or when tracking user actions across
266
+ * different sessions.
267
+ *
268
+ * The resolver is stored in a key-value store with a TTL (time to live), and will be used
269
+ * to resolve the user ID when operations are performed with the given session ID. If the resolver
270
+ * expires, it will need to be registered again.
271
+ *
272
+ * This feature works in conjunction with the SessionPathwayBuilder to provide a complete
273
+ * session management solution.
274
+ *
255
275
  * @param sessionId The session ID to associate with this resolver
256
276
  * @param resolver The resolver function that resolves to the user ID for this session
257
277
  * @returns The PathwaysBuilder instance for chaining
278
+ *
279
+ * @throws Error if session user resolvers are not configured (sessionUserResolvers not provided in constructor)
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * // Register a resolver for a specific session
284
+ * pathwaysBuilder.withSessionUserResolver("session-123", async () => {
285
+ * return "user-456"; // Return the user ID for this session
286
+ * });
287
+ *
288
+ * // Use with SessionPathwayBuilder
289
+ * const session = new SessionPathwayBuilder(pathwaysBuilder, "session-123");
290
+ * await session.write("user/action", actionData);
291
+ * // The user ID will be automatically included in the metadata
292
+ * ```
258
293
  */
259
294
  withSessionUserResolver(sessionId, resolver) {
295
+ if (!this.sessionUserResolvers) {
296
+ throw new Error('Session user resolvers not configured');
297
+ }
260
298
  this.logger.debug('Configuring session-specific user resolver', { sessionId });
261
- this.sessionUserResolvers.set(sessionId, resolver);
299
+ this.sessionUserResolvers.set(sessionId, resolver, DEFAULT_SESSION_USER_RESOLVER_TTL_MS);
262
300
  return this;
263
301
  }
264
302
  /**
@@ -267,7 +305,11 @@ class PathwaysBuilder {
267
305
  * @returns The resolver function for the session, or undefined if none exists
268
306
  */
269
307
  getSessionUserResolver(sessionId) {
270
- return this.sessionUserResolvers.get(sessionId);
308
+ if (!this.sessionUserResolvers) {
309
+ return undefined;
310
+ }
311
+ const resolver = this.sessionUserResolvers.get(sessionId);
312
+ return resolver;
271
313
  }
272
314
  /**
273
315
  * Process a pathway event with error handling and retries
@@ -591,7 +633,7 @@ class PathwaysBuilder {
591
633
  // Check for session-specific user resolver
592
634
  let userId;
593
635
  if (options?.sessionId) {
594
- const sessionUserResolver = this.sessionUserResolvers.get(options.sessionId);
636
+ const sessionUserResolver = this.getSessionUserResolver(options.sessionId);
595
637
  if (sessionUserResolver) {
596
638
  try {
597
639
  userId = await sessionUserResolver();
@@ -2,7 +2,34 @@
2
2
  * Interface for key-value storage adapters
3
3
  *
4
4
  * Provides a common interface for different KV storage implementations
5
- * that can be used for storing pathway state.
5
+ * that can be used for storing pathway state and other application data.
6
+ *
7
+ * This interface abstracts away the details of specific storage backends,
8
+ * allowing the application to work with different storage providers
9
+ * without changing the core logic.
10
+ *
11
+ * The Flowcore Pathways library includes several implementations of this interface:
12
+ * - BunKvAdapter: Uses Bun's built-in KV store
13
+ * - NodeKvAdapter: Uses node-cache for in-memory storage
14
+ * - DenoKvAdapter: Uses Deno's KV store (when available)
15
+ *
16
+ * Custom implementations can be created for other storage backends
17
+ * by implementing this interface.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Create a KV adapter
22
+ * const store = await createKvAdapter();
23
+ *
24
+ * // Store a value with a TTL
25
+ * await store.set("session:123", { userId: "user-456" }, 3600000); // 1 hour TTL
26
+ *
27
+ * // Retrieve the value
28
+ * const session = await store.get<{ userId: string }>("session:123");
29
+ * if (session) {
30
+ * console.log("User ID:", session.userId);
31
+ * }
32
+ * ```
6
33
  */
7
34
  export interface KvAdapter {
8
35
  /**
@@ -26,9 +53,39 @@ export interface KvAdapter {
26
53
  /**
27
54
  * Creates an appropriate KV adapter based on the runtime environment
28
55
  *
29
- * Attempts to use Bun KV adapter if running in Bun, falls back to Node adapter otherwise
56
+ * This function automatically detects the current runtime environment and creates
57
+ * the most suitable KV adapter implementation:
58
+ *
59
+ * - In Bun: Returns a BunKvAdapter using Bun's built-in KV store
60
+ * - In Deno with KV access: Returns a DenoKvAdapter
61
+ * - Otherwise: Returns a NodeKvAdapter using an in-memory cache
62
+ *
63
+ * Using this factory function rather than directly instantiating a specific adapter
64
+ * implementation makes your code more portable across different JavaScript runtimes.
65
+ *
66
+ * The adapter is lazily initialized, so any necessary setup only happens when
67
+ * you first interact with the adapter.
30
68
  *
31
69
  * @returns A KV adapter instance for the current runtime
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Create a runtime-specific KV adapter
74
+ * const kv = await createKvAdapter();
75
+ *
76
+ * // Use with PathwaysBuilder for session user resolvers
77
+ * const pathways = new PathwaysBuilder({
78
+ * baseUrl: "https://api.flowcore.io",
79
+ * tenant: "my-tenant",
80
+ * dataCore: "my-data-core",
81
+ * apiKey: "my-api-key",
82
+ * sessionUserResolvers: kv
83
+ * });
84
+ *
85
+ * // Use as a general-purpose key-value store
86
+ * await kv.set("cache:user:123", userData, 60 * 60 * 1000); // 1 hour TTL
87
+ * const cachedUser = await kv.get("cache:user:123");
88
+ * ```
32
89
  */
33
90
  export declare function createKvAdapter(): Promise<KvAdapter>;
34
91
  //# sourceMappingURL=kv-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kv-adapter.d.ts","sourceRoot":"","sources":["../../../src/pathways/kv/kv-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAEpD;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;CACzE;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAU1D"}
1
+ {"version":3,"file":"kv-adapter.d.ts","sourceRoot":"","sources":["../../../src/pathways/kv/kv-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAEpD;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;CACzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAU1D"}
@@ -27,9 +27,39 @@ exports.createKvAdapter = createKvAdapter;
27
27
  /**
28
28
  * Creates an appropriate KV adapter based on the runtime environment
29
29
  *
30
- * Attempts to use Bun KV adapter if running in Bun, falls back to Node adapter otherwise
30
+ * This function automatically detects the current runtime environment and creates
31
+ * the most suitable KV adapter implementation:
32
+ *
33
+ * - In Bun: Returns a BunKvAdapter using Bun's built-in KV store
34
+ * - In Deno with KV access: Returns a DenoKvAdapter
35
+ * - Otherwise: Returns a NodeKvAdapter using an in-memory cache
36
+ *
37
+ * Using this factory function rather than directly instantiating a specific adapter
38
+ * implementation makes your code more portable across different JavaScript runtimes.
39
+ *
40
+ * The adapter is lazily initialized, so any necessary setup only happens when
41
+ * you first interact with the adapter.
31
42
  *
32
43
  * @returns A KV adapter instance for the current runtime
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Create a runtime-specific KV adapter
48
+ * const kv = await createKvAdapter();
49
+ *
50
+ * // Use with PathwaysBuilder for session user resolvers
51
+ * const pathways = new PathwaysBuilder({
52
+ * baseUrl: "https://api.flowcore.io",
53
+ * tenant: "my-tenant",
54
+ * dataCore: "my-data-core",
55
+ * apiKey: "my-api-key",
56
+ * sessionUserResolvers: kv
57
+ * });
58
+ *
59
+ * // Use as a general-purpose key-value store
60
+ * await kv.set("cache:user:123", userData, 60 * 60 * 1000); // 1 hour TTL
61
+ * const cachedUser = await kv.get("cache:user:123");
62
+ * ```
33
63
  */
34
64
  async function createKvAdapter() {
35
65
  try {
@@ -53,6 +53,47 @@ export type PostgresPathwayStateConfig = PostgresPathwayStateConnectionStringCon
53
53
  *
54
54
  * This class provides persistent storage of pathway state using a PostgreSQL database,
55
55
  * which allows for state to be shared across multiple instances of the application.
56
+ *
57
+ * Key features:
58
+ * - Persistent storage of pathway processing state across application restarts
59
+ * - Automatic table and index creation
60
+ * - Configurable TTL (time-to-live) for processed events
61
+ * - Automatic cleanup of expired records
62
+ * - Support for horizontal scaling across multiple instances
63
+ * - Connection pooling for efficient database usage
64
+ *
65
+ * Use cases:
66
+ * - Production deployments that require durability and persistence
67
+ * - Distributed systems where multiple instances process events
68
+ * - Applications with high reliability requirements
69
+ * - Scenarios where in-memory state is insufficient
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Create PostgreSQL pathway state with connection string
74
+ * const postgresState = createPostgresPathwayState({
75
+ * connectionString: "postgres://user:password@localhost:5432/mydb",
76
+ * tableName: "event_processing_state", // Optional
77
+ * ttlMs: 24 * 60 * 60 * 1000 // 24 hours (optional)
78
+ * });
79
+ *
80
+ * // Or with individual parameters
81
+ * const postgresState = createPostgresPathwayState({
82
+ * host: "localhost",
83
+ * port: 5432,
84
+ * user: "postgres",
85
+ * password: "postgres",
86
+ * database: "mydb",
87
+ * ssl: false,
88
+ * tableName: "event_processing_state", // Optional
89
+ * ttlMs: 30 * 60 * 1000 // 30 minutes (optional)
90
+ * });
91
+ *
92
+ * // Use with PathwaysBuilder
93
+ * const pathways = new PathwaysBuilder({
94
+ * // ... other config
95
+ * }).withPathwayState(postgresState);
96
+ * ```
56
97
  */
57
98
  export declare class PostgresPathwayState implements PathwayState {
58
99
  private config;
@@ -102,15 +143,66 @@ export declare class PostgresPathwayState implements PathwayState {
102
143
  /**
103
144
  * Checks if an event has already been processed
104
145
  *
146
+ * This method checks the PostgreSQL database to determine if an event with the given ID
147
+ * has been marked as processed. If the event exists in the database and is marked as processed,
148
+ * the method returns true.
149
+ *
150
+ * Before performing the check, this method also triggers cleanup of expired event records
151
+ * to maintain database performance.
152
+ *
105
153
  * @param {string} eventId - The ID of the event to check
106
154
  * @returns {Promise<boolean>} True if the event has been processed, false otherwise
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // Check if an event has been processed
159
+ * const processed = await postgresState.isProcessed("event-123");
160
+ * if (processed) {
161
+ * console.log("Event has already been processed, skipping");
162
+ * } else {
163
+ * console.log("Processing event for the first time");
164
+ * // Process the event
165
+ * await processEvent(event);
166
+ * // Mark as processed
167
+ * await postgresState.setProcessed("event-123");
168
+ * }
169
+ * ```
107
170
  */
108
171
  isProcessed(eventId: string): Promise<boolean>;
109
172
  /**
110
173
  * Marks an event as processed
111
174
  *
175
+ * This method inserts or updates a record in the PostgreSQL database to mark an event
176
+ * as processed. If the event already exists in the database, the record is updated;
177
+ * otherwise, a new record is created.
178
+ *
179
+ * Each processed event is stored with an expiration timestamp based on the configured TTL.
180
+ * After this time elapses, the record may be automatically removed during cleanup operations.
181
+ *
112
182
  * @param {string} eventId - The ID of the event to mark as processed
113
183
  * @returns {Promise<void>}
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // Process an event and mark it as processed
188
+ * async function handleEvent(event) {
189
+ * // Check if already processed to implement idempotency
190
+ * if (await postgresState.isProcessed(event.id)) {
191
+ * return; // Skip already processed events
192
+ * }
193
+ *
194
+ * try {
195
+ * // Process the event
196
+ * await processEvent(event);
197
+ *
198
+ * // Mark as processed after successful processing
199
+ * await postgresState.setProcessed(event.id);
200
+ * } catch (error) {
201
+ * console.error("Failed to process event:", error);
202
+ * // Not marking as processed, so it can be retried
203
+ * }
204
+ * }
205
+ * ```
114
206
  */
115
207
  setProcessed(eventId: string): Promise<void>;
116
208
  /**
@@ -130,8 +222,46 @@ export declare class PostgresPathwayState implements PathwayState {
130
222
  /**
131
223
  * Creates a new PostgreSQL pathway state instance
132
224
  *
133
- * @param config The PostgreSQL configuration
225
+ * This is a factory function that simplifies the creation of PostgresPathwayState instances.
226
+ * It accepts either a connection string or individual connection parameters, along with
227
+ * optional configuration for table name and TTL.
228
+ *
229
+ * The PostgresPathwayState is lazily initialized, meaning the database connection and
230
+ * table creation only happen when the first operation is performed. This makes it safe
231
+ * to create instances early in the application lifecycle.
232
+ *
233
+ * @param config The PostgreSQL configuration (connection string or parameters)
134
234
  * @returns A new PostgresPathwayState instance
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // With connection string
239
+ * const state = createPostgresPathwayState({
240
+ * connectionString: "postgres://user:pass@localhost:5432/db?sslmode=require"
241
+ * });
242
+ *
243
+ * // With individual parameters
244
+ * const state = createPostgresPathwayState({
245
+ * host: "localhost",
246
+ * port: 5432,
247
+ * user: "postgres",
248
+ * password: "secret",
249
+ * database: "events_db",
250
+ * ssl: true
251
+ * });
252
+ *
253
+ * // With custom table name and TTL
254
+ * const state = createPostgresPathwayState({
255
+ * connectionString: "postgres://user:pass@localhost:5432/db",
256
+ * tableName: "my_custom_event_state",
257
+ * ttlMs: 7 * 24 * 60 * 60 * 1000 // 1 week
258
+ * });
259
+ *
260
+ * // Use with PathwaysBuilder
261
+ * const pathways = new PathwaysBuilder({
262
+ * // Other config
263
+ * }).withPathwayState(state);
264
+ * ```
135
265
  */
136
266
  export declare function createPostgresPathwayState(config: PostgresPathwayStateConfig): PostgresPathwayState;
137
267
  //# sourceMappingURL=postgres-pathway-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-pathway-state.d.ts","sourceRoot":"","sources":["../../../src/pathways/postgres/postgres-pathway-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;GAEG;AACH,MAAM,WAAW,0CAA0C;IACzD,gHAAgH;IAChH,gBAAgB,EAAE,MAAM,CAAC;IAEzB,yEAAyE;IACzE,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,GAAG,CAAC,EAAE,KAAK,CAAC;IAEZ,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACnD,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,KAAK,CAAC;IAEzB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,0CAA0C,GAAG,oCAAoC,CAAC;AAE3H;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IA0C3C,OAAO,CAAC,MAAM;IAzC1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAEvD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IAE7D;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAkB;IAElC;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAS;IAE1B;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAS;IAEtB;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;OAIG;gBACiB,MAAM,EAAE,0BAA0B;IAMtD;;;;;OAKG;YACW,UAAU;IA0CxB;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcpD;;;;;OAKG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelD;;;;;OAKG;YACW,cAAc;IAQ5B;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAGnG"}
1
+ {"version":3,"file":"postgres-pathway-state.d.ts","sourceRoot":"","sources":["../../../src/pathways/postgres/postgres-pathway-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;GAEG;AACH,MAAM,WAAW,0CAA0C;IACzD,gHAAgH;IAChH,gBAAgB,EAAE,MAAM,CAAC;IAEzB,yEAAyE;IACzE,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,GAAG,CAAC,EAAE,KAAK,CAAC;IAEZ,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACnD,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,KAAK,CAAC;IAEzB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,0CAA0C,GAAG,oCAAoC,CAAC;AAE3H;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IA0C3C,OAAO,CAAC,MAAM;IAzC1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAEvD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IAE7D;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAkB;IAElC;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAS;IAE1B;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAS;IAEtB;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;OAIG;gBACiB,MAAM,EAAE,0BAA0B;IAMtD;;;;;OAKG;YACW,UAAU;IA0CxB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelD;;;;;OAKG;YACW,cAAc;IAQ5B;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAGnG"}
@@ -8,6 +8,47 @@ const postgres_adapter_js_1 = require("./postgres-adapter.js");
8
8
  *
9
9
  * This class provides persistent storage of pathway state using a PostgreSQL database,
10
10
  * which allows for state to be shared across multiple instances of the application.
11
+ *
12
+ * Key features:
13
+ * - Persistent storage of pathway processing state across application restarts
14
+ * - Automatic table and index creation
15
+ * - Configurable TTL (time-to-live) for processed events
16
+ * - Automatic cleanup of expired records
17
+ * - Support for horizontal scaling across multiple instances
18
+ * - Connection pooling for efficient database usage
19
+ *
20
+ * Use cases:
21
+ * - Production deployments that require durability and persistence
22
+ * - Distributed systems where multiple instances process events
23
+ * - Applications with high reliability requirements
24
+ * - Scenarios where in-memory state is insufficient
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Create PostgreSQL pathway state with connection string
29
+ * const postgresState = createPostgresPathwayState({
30
+ * connectionString: "postgres://user:password@localhost:5432/mydb",
31
+ * tableName: "event_processing_state", // Optional
32
+ * ttlMs: 24 * 60 * 60 * 1000 // 24 hours (optional)
33
+ * });
34
+ *
35
+ * // Or with individual parameters
36
+ * const postgresState = createPostgresPathwayState({
37
+ * host: "localhost",
38
+ * port: 5432,
39
+ * user: "postgres",
40
+ * password: "postgres",
41
+ * database: "mydb",
42
+ * ssl: false,
43
+ * tableName: "event_processing_state", // Optional
44
+ * ttlMs: 30 * 60 * 1000 // 30 minutes (optional)
45
+ * });
46
+ *
47
+ * // Use with PathwaysBuilder
48
+ * const pathways = new PathwaysBuilder({
49
+ * // ... other config
50
+ * }).withPathwayState(postgresState);
51
+ * ```
11
52
  */
12
53
  class PostgresPathwayState {
13
54
  /**
@@ -113,8 +154,30 @@ class PostgresPathwayState {
113
154
  /**
114
155
  * Checks if an event has already been processed
115
156
  *
157
+ * This method checks the PostgreSQL database to determine if an event with the given ID
158
+ * has been marked as processed. If the event exists in the database and is marked as processed,
159
+ * the method returns true.
160
+ *
161
+ * Before performing the check, this method also triggers cleanup of expired event records
162
+ * to maintain database performance.
163
+ *
116
164
  * @param {string} eventId - The ID of the event to check
117
165
  * @returns {Promise<boolean>} True if the event has been processed, false otherwise
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * // Check if an event has been processed
170
+ * const processed = await postgresState.isProcessed("event-123");
171
+ * if (processed) {
172
+ * console.log("Event has already been processed, skipping");
173
+ * } else {
174
+ * console.log("Processing event for the first time");
175
+ * // Process the event
176
+ * await processEvent(event);
177
+ * // Mark as processed
178
+ * await postgresState.setProcessed("event-123");
179
+ * }
180
+ * ```
118
181
  */
119
182
  async isProcessed(eventId) {
120
183
  await this.initialize();
@@ -129,8 +192,37 @@ class PostgresPathwayState {
129
192
  /**
130
193
  * Marks an event as processed
131
194
  *
195
+ * This method inserts or updates a record in the PostgreSQL database to mark an event
196
+ * as processed. If the event already exists in the database, the record is updated;
197
+ * otherwise, a new record is created.
198
+ *
199
+ * Each processed event is stored with an expiration timestamp based on the configured TTL.
200
+ * After this time elapses, the record may be automatically removed during cleanup operations.
201
+ *
132
202
  * @param {string} eventId - The ID of the event to mark as processed
133
203
  * @returns {Promise<void>}
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * // Process an event and mark it as processed
208
+ * async function handleEvent(event) {
209
+ * // Check if already processed to implement idempotency
210
+ * if (await postgresState.isProcessed(event.id)) {
211
+ * return; // Skip already processed events
212
+ * }
213
+ *
214
+ * try {
215
+ * // Process the event
216
+ * await processEvent(event);
217
+ *
218
+ * // Mark as processed after successful processing
219
+ * await postgresState.setProcessed(event.id);
220
+ * } catch (error) {
221
+ * console.error("Failed to process event:", error);
222
+ * // Not marking as processed, so it can be retried
223
+ * }
224
+ * }
225
+ * ```
134
226
  */
135
227
  async setProcessed(eventId) {
136
228
  await this.initialize();
@@ -193,8 +285,46 @@ Object.defineProperty(PostgresPathwayState, "DEFAULT_TABLE_NAME", {
193
285
  /**
194
286
  * Creates a new PostgreSQL pathway state instance
195
287
  *
196
- * @param config The PostgreSQL configuration
288
+ * This is a factory function that simplifies the creation of PostgresPathwayState instances.
289
+ * It accepts either a connection string or individual connection parameters, along with
290
+ * optional configuration for table name and TTL.
291
+ *
292
+ * The PostgresPathwayState is lazily initialized, meaning the database connection and
293
+ * table creation only happen when the first operation is performed. This makes it safe
294
+ * to create instances early in the application lifecycle.
295
+ *
296
+ * @param config The PostgreSQL configuration (connection string or parameters)
197
297
  * @returns A new PostgresPathwayState instance
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * // With connection string
302
+ * const state = createPostgresPathwayState({
303
+ * connectionString: "postgres://user:pass@localhost:5432/db?sslmode=require"
304
+ * });
305
+ *
306
+ * // With individual parameters
307
+ * const state = createPostgresPathwayState({
308
+ * host: "localhost",
309
+ * port: 5432,
310
+ * user: "postgres",
311
+ * password: "secret",
312
+ * database: "events_db",
313
+ * ssl: true
314
+ * });
315
+ *
316
+ * // With custom table name and TTL
317
+ * const state = createPostgresPathwayState({
318
+ * connectionString: "postgres://user:pass@localhost:5432/db",
319
+ * tableName: "my_custom_event_state",
320
+ * ttlMs: 7 * 24 * 60 * 60 * 1000 // 1 week
321
+ * });
322
+ *
323
+ * // Use with PathwaysBuilder
324
+ * const pathways = new PathwaysBuilder({
325
+ * // Other config
326
+ * }).withPathwayState(state);
327
+ * ```
198
328
  */
199
329
  function createPostgresPathwayState(config) {
200
330
  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;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqBrE;;;;;;GAMG;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;;;;;;;;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"}
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"}