@flowcore/pathways 0.2.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 +36 -0
- package/README.md +420 -0
- package/esm/common/flowcore.type.d.ts +11 -0
- package/esm/common/flowcore.type.d.ts.map +1 -0
- package/esm/common/flowcore.type.js +1 -0
- package/esm/common/index.d.ts +7 -0
- package/esm/common/index.d.ts.map +1 -0
- package/esm/common/index.js +6 -0
- package/esm/compatibility/flowcore-transformer-core.sdk.d.ts +16 -0
- package/esm/compatibility/flowcore-transformer-core.sdk.d.ts.map +1 -0
- package/esm/compatibility/flowcore-transformer-core.sdk.js +11 -0
- package/esm/contracts/event.d.ts +20 -0
- package/esm/contracts/event.d.ts.map +1 -0
- package/esm/contracts/event.js +15 -0
- package/esm/contracts/index.d.ts +5 -0
- package/esm/contracts/index.d.ts.map +1 -0
- package/esm/contracts/index.js +4 -0
- package/esm/mod.d.ts +14 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +13 -0
- package/esm/package.json +3 -0
- package/esm/pathways/builder.d.ts +91 -0
- package/esm/pathways/builder.d.ts.map +1 -0
- package/esm/pathways/builder.js +530 -0
- package/esm/pathways/index.d.ts +17 -0
- package/esm/pathways/index.d.ts.map +1 -0
- package/esm/pathways/index.js +16 -0
- package/esm/pathways/internal-pathway.state.d.ts +41 -0
- package/esm/pathways/internal-pathway.state.d.ts.map +1 -0
- package/esm/pathways/internal-pathway.state.js +64 -0
- package/esm/pathways/kv/bun-kv-adapter.d.ts +36 -0
- package/esm/pathways/kv/bun-kv-adapter.d.ts.map +1 -0
- package/esm/pathways/kv/bun-kv-adapter.js +47 -0
- package/esm/pathways/kv/kv-adapter.d.ts +34 -0
- package/esm/pathways/kv/kv-adapter.d.ts.map +1 -0
- package/esm/pathways/kv/kv-adapter.js +19 -0
- package/esm/pathways/kv/node-kv-adapter.d.ts +33 -0
- package/esm/pathways/kv/node-kv-adapter.d.ts.map +1 -0
- package/esm/pathways/kv/node-kv-adapter.js +44 -0
- package/esm/pathways/logger.d.ts +48 -0
- package/esm/pathways/logger.d.ts.map +1 -0
- package/esm/pathways/logger.js +26 -0
- package/esm/pathways/postgres/index.d.ts +9 -0
- package/esm/pathways/postgres/index.d.ts.map +1 -0
- package/esm/pathways/postgres/index.js +8 -0
- package/esm/pathways/postgres/postgres-adapter.d.ts +112 -0
- package/esm/pathways/postgres/postgres-adapter.d.ts.map +1 -0
- package/esm/pathways/postgres/postgres-adapter.js +113 -0
- package/esm/pathways/postgres/postgres-pathway-state.d.ts +113 -0
- package/esm/pathways/postgres/postgres-pathway-state.d.ts.map +1 -0
- package/esm/pathways/postgres/postgres-pathway-state.js +188 -0
- package/esm/pathways/types.d.ts +87 -0
- package/esm/pathways/types.d.ts.map +1 -0
- package/esm/pathways/types.js +1 -0
- package/esm/router/index.d.ts +35 -0
- package/esm/router/index.d.ts.map +1 -0
- package/esm/router/index.js +96 -0
- package/package.json +38 -0
- package/script/common/flowcore.type.d.ts +11 -0
- package/script/common/flowcore.type.d.ts.map +1 -0
- package/script/common/flowcore.type.js +2 -0
- package/script/common/index.d.ts +7 -0
- package/script/common/index.d.ts.map +1 -0
- package/script/common/index.js +22 -0
- package/script/compatibility/flowcore-transformer-core.sdk.d.ts +16 -0
- package/script/compatibility/flowcore-transformer-core.sdk.d.ts.map +1 -0
- package/script/compatibility/flowcore-transformer-core.sdk.js +17 -0
- package/script/contracts/event.d.ts +20 -0
- package/script/contracts/event.d.ts.map +1 -0
- package/script/contracts/event.js +18 -0
- package/script/contracts/index.d.ts +5 -0
- package/script/contracts/index.d.ts.map +1 -0
- package/script/contracts/index.js +20 -0
- package/script/mod.d.ts +14 -0
- package/script/mod.d.ts.map +1 -0
- package/script/mod.js +29 -0
- package/script/package.json +3 -0
- package/script/pathways/builder.d.ts +91 -0
- package/script/pathways/builder.d.ts.map +1 -0
- package/script/pathways/builder.js +534 -0
- package/script/pathways/index.d.ts +17 -0
- package/script/pathways/index.d.ts.map +1 -0
- package/script/pathways/index.js +32 -0
- package/script/pathways/internal-pathway.state.d.ts +41 -0
- package/script/pathways/internal-pathway.state.d.ts.map +1 -0
- package/script/pathways/internal-pathway.state.js +68 -0
- package/script/pathways/kv/bun-kv-adapter.d.ts +36 -0
- package/script/pathways/kv/bun-kv-adapter.d.ts.map +1 -0
- package/script/pathways/kv/bun-kv-adapter.js +51 -0
- package/script/pathways/kv/kv-adapter.d.ts +34 -0
- package/script/pathways/kv/kv-adapter.d.ts.map +1 -0
- package/script/pathways/kv/kv-adapter.js +45 -0
- package/script/pathways/kv/node-kv-adapter.d.ts +33 -0
- package/script/pathways/kv/node-kv-adapter.d.ts.map +1 -0
- package/script/pathways/kv/node-kv-adapter.js +51 -0
- package/script/pathways/logger.d.ts +48 -0
- package/script/pathways/logger.d.ts.map +1 -0
- package/script/pathways/logger.js +31 -0
- package/script/pathways/postgres/index.d.ts +9 -0
- package/script/pathways/postgres/index.d.ts.map +1 -0
- package/script/pathways/postgres/index.js +24 -0
- package/script/pathways/postgres/postgres-adapter.d.ts +112 -0
- package/script/pathways/postgres/postgres-adapter.d.ts.map +1 -0
- package/script/pathways/postgres/postgres-adapter.js +141 -0
- package/script/pathways/postgres/postgres-pathway-state.d.ts +113 -0
- package/script/pathways/postgres/postgres-pathway-state.d.ts.map +1 -0
- package/script/pathways/postgres/postgres-pathway-state.js +193 -0
- package/script/pathways/types.d.ts +87 -0
- package/script/pathways/types.d.ts.map +1 -0
- package/script/pathways/types.js +2 -0
- package/script/router/index.d.ts +35 -0
- package/script/router/index.d.ts.map +1 -0
- package/script/router/index.js +100 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PathwaysBuilder = void 0;
|
|
4
|
+
const value_1 = require("@sinclair/typebox/value");
|
|
5
|
+
const rxjs_1 = require("rxjs");
|
|
6
|
+
const flowcore_transformer_core_sdk_js_1 = require("../compatibility/flowcore-transformer-core.sdk.js");
|
|
7
|
+
const internal_pathway_state_js_1 = require("./internal-pathway.state.js");
|
|
8
|
+
const logger_js_1 = require("./logger.js");
|
|
9
|
+
const DEFAULT_PATHWAY_TIMEOUT_MS = 10000;
|
|
10
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
11
|
+
const DEFAULT_RETRY_DELAY_MS = 500;
|
|
12
|
+
class PathwaysBuilder {
|
|
13
|
+
constructor({ baseUrl, tenant, dataCore, apiKey, pathwayTimeoutMs, logger, }) {
|
|
14
|
+
Object.defineProperty(this, "pathways", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: {}
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "handlers", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: {}
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "beforeObservable", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: {}
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "afterObservers", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: {}
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "errorObservers", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: {}
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "globalErrorSubject", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: new rxjs_1.Subject()
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "writers", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: {}
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "schemas", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: {}
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "writable", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: {}
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "timeouts", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: {}
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(this, "maxRetries", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
value: {}
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(this, "retryDelays", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
value: {}
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(this, "filePathways", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
value: new Set()
|
|
91
|
+
});
|
|
92
|
+
Object.defineProperty(this, "webhookBuilderFactory", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true,
|
|
95
|
+
writable: true,
|
|
96
|
+
value: void 0
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(this, "pathwayState", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: true,
|
|
101
|
+
writable: true,
|
|
102
|
+
value: new internal_pathway_state_js_1.InternalPathwayState()
|
|
103
|
+
});
|
|
104
|
+
Object.defineProperty(this, "pathwayTimeoutMs", {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value: DEFAULT_PATHWAY_TIMEOUT_MS
|
|
109
|
+
});
|
|
110
|
+
// Audit-related properties
|
|
111
|
+
Object.defineProperty(this, "auditHandler", {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: true,
|
|
114
|
+
writable: true,
|
|
115
|
+
value: void 0
|
|
116
|
+
});
|
|
117
|
+
Object.defineProperty(this, "userIdResolver", {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
configurable: true,
|
|
120
|
+
writable: true,
|
|
121
|
+
value: void 0
|
|
122
|
+
});
|
|
123
|
+
// Logger instance (but not using it yet due to TypeScript errors)
|
|
124
|
+
Object.defineProperty(this, "logger", {
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true,
|
|
127
|
+
writable: true,
|
|
128
|
+
value: void 0
|
|
129
|
+
});
|
|
130
|
+
// Initialize logger (use NoopLogger if none provided)
|
|
131
|
+
this.logger = logger ?? new logger_js_1.NoopLogger();
|
|
132
|
+
this.logger.debug('Initializing PathwaysBuilder', {
|
|
133
|
+
baseUrl,
|
|
134
|
+
tenant,
|
|
135
|
+
dataCore,
|
|
136
|
+
pathwayTimeoutMs
|
|
137
|
+
});
|
|
138
|
+
this.webhookBuilderFactory = new flowcore_transformer_core_sdk_js_1.WebhookBuilder({
|
|
139
|
+
baseUrl,
|
|
140
|
+
tenant,
|
|
141
|
+
dataCore,
|
|
142
|
+
apiKey,
|
|
143
|
+
})
|
|
144
|
+
.withRetry({
|
|
145
|
+
maxAttempts: 5,
|
|
146
|
+
attemptDelayMs: 250,
|
|
147
|
+
})
|
|
148
|
+
.factory();
|
|
149
|
+
if (pathwayTimeoutMs) {
|
|
150
|
+
this.pathwayTimeoutMs = pathwayTimeoutMs;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
withPathwayState(state) {
|
|
154
|
+
this.logger.debug('Setting custom pathway state');
|
|
155
|
+
this.pathwayState = state;
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Configures the PathwaysBuilder to use audit functionality
|
|
160
|
+
* @param handler The handler function that receives pathway and event information
|
|
161
|
+
* @param userIdResolver An async function that resolves to the current user ID
|
|
162
|
+
* @returns The PathwaysBuilder instance with audit configured
|
|
163
|
+
*/
|
|
164
|
+
withAudit(handler, userIdResolver) {
|
|
165
|
+
this.logger.debug('Configuring audit functionality');
|
|
166
|
+
this.auditHandler = handler;
|
|
167
|
+
this.userIdResolver = userIdResolver;
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Process a pathway event with error handling and retries
|
|
172
|
+
* @param pathway The pathway to process
|
|
173
|
+
* @param data The event data to process
|
|
174
|
+
* @returns Promise that resolves when processing is complete
|
|
175
|
+
*/
|
|
176
|
+
async process(pathway, data) {
|
|
177
|
+
const pathwayStr = String(pathway);
|
|
178
|
+
this.logger.debug(`Processing pathway event`, {
|
|
179
|
+
pathway: pathwayStr,
|
|
180
|
+
eventId: data.eventId
|
|
181
|
+
});
|
|
182
|
+
if (!this.pathways[pathway]) {
|
|
183
|
+
const error = `Pathway ${pathwayStr} not found`;
|
|
184
|
+
this.logger.error(error);
|
|
185
|
+
throw new Error(error);
|
|
186
|
+
}
|
|
187
|
+
// Call audit handler if configured
|
|
188
|
+
if (this.auditHandler) {
|
|
189
|
+
this.logger.debug(`Calling audit handler for pathway`, {
|
|
190
|
+
pathway: pathwayStr,
|
|
191
|
+
eventId: data.eventId
|
|
192
|
+
});
|
|
193
|
+
this.auditHandler(pathwayStr, data);
|
|
194
|
+
}
|
|
195
|
+
if (this.handlers[pathway]) {
|
|
196
|
+
let retryCount = 0;
|
|
197
|
+
const maxRetries = this.maxRetries[pathway] ?? DEFAULT_MAX_RETRIES;
|
|
198
|
+
const retryDelayMs = this.retryDelays[pathway] ?? DEFAULT_RETRY_DELAY_MS;
|
|
199
|
+
this.logger.debug(`Emitting 'before' event`, {
|
|
200
|
+
pathway: pathwayStr,
|
|
201
|
+
eventId: data.eventId
|
|
202
|
+
});
|
|
203
|
+
this.beforeObservable[pathway].next(data);
|
|
204
|
+
while (true) {
|
|
205
|
+
try {
|
|
206
|
+
this.logger.debug(`Executing handler for pathway`, {
|
|
207
|
+
pathway: pathwayStr,
|
|
208
|
+
eventId: data.eventId,
|
|
209
|
+
attempt: retryCount + 1
|
|
210
|
+
});
|
|
211
|
+
// Execute the handler
|
|
212
|
+
const handle = this.handlers[pathway](data);
|
|
213
|
+
await handle;
|
|
214
|
+
// If successful, emit success event and mark as processed
|
|
215
|
+
this.logger.debug(`Handler executed successfully, emitting 'after' event`, {
|
|
216
|
+
pathway: pathwayStr,
|
|
217
|
+
eventId: data.eventId
|
|
218
|
+
});
|
|
219
|
+
this.afterObservers[pathway].next(data);
|
|
220
|
+
await this.pathwayState.setProcessed(data.eventId);
|
|
221
|
+
this.logger.info(`Successfully processed pathway event`, {
|
|
222
|
+
pathway: pathwayStr,
|
|
223
|
+
eventId: data.eventId
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
// Create error object if needed
|
|
229
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
230
|
+
this.logger.error(`Error processing pathway event`, {
|
|
231
|
+
pathway: pathwayStr,
|
|
232
|
+
eventId: data.eventId,
|
|
233
|
+
error: errorObj.message,
|
|
234
|
+
retryCount,
|
|
235
|
+
maxRetries
|
|
236
|
+
});
|
|
237
|
+
// Emit error event with both error and event data
|
|
238
|
+
this.errorObservers[pathway].next({ event: data, error: errorObj });
|
|
239
|
+
// Also emit to global error subject
|
|
240
|
+
this.globalErrorSubject.next({
|
|
241
|
+
pathway: pathwayStr,
|
|
242
|
+
event: data,
|
|
243
|
+
error: errorObj
|
|
244
|
+
});
|
|
245
|
+
// Check if we should retry
|
|
246
|
+
if (retryCount < maxRetries) {
|
|
247
|
+
retryCount++;
|
|
248
|
+
const nextDelay = retryDelayMs * retryCount;
|
|
249
|
+
this.logger.debug(`Retrying pathway event processing`, {
|
|
250
|
+
pathway: pathwayStr,
|
|
251
|
+
eventId: data.eventId,
|
|
252
|
+
attempt: retryCount,
|
|
253
|
+
maxRetries,
|
|
254
|
+
nextDelay
|
|
255
|
+
});
|
|
256
|
+
// Wait for delay before retrying
|
|
257
|
+
await new Promise(resolve => setTimeout(resolve, nextDelay));
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
// If we've exhausted retries, mark as processed to avoid hanging
|
|
261
|
+
this.logger.warn(`Max retries exceeded for pathway event, marking as processed`, {
|
|
262
|
+
pathway: pathwayStr,
|
|
263
|
+
eventId: data.eventId,
|
|
264
|
+
retryCount,
|
|
265
|
+
maxRetries
|
|
266
|
+
});
|
|
267
|
+
await this.pathwayState.setProcessed(data.eventId);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// No handler, just emit events and mark as processed
|
|
274
|
+
this.logger.debug(`No handler for pathway, emitting events and marking as processed`, {
|
|
275
|
+
pathway: pathwayStr,
|
|
276
|
+
eventId: data.eventId
|
|
277
|
+
});
|
|
278
|
+
this.beforeObservable[pathway].next(data);
|
|
279
|
+
this.afterObservers[pathway].next(data);
|
|
280
|
+
await this.pathwayState.setProcessed(data.eventId);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
register(contract) {
|
|
284
|
+
const path = `${contract.flowType}/${contract.eventType}`;
|
|
285
|
+
const writable = contract.writable ?? true;
|
|
286
|
+
this.logger.debug(`Registering pathway`, {
|
|
287
|
+
pathway: path,
|
|
288
|
+
flowType: contract.flowType,
|
|
289
|
+
eventType: contract.eventType,
|
|
290
|
+
writable,
|
|
291
|
+
isFilePathway: contract.isFilePathway,
|
|
292
|
+
timeoutMs: contract.timeoutMs,
|
|
293
|
+
maxRetries: contract.maxRetries,
|
|
294
|
+
retryDelayMs: contract.retryDelayMs
|
|
295
|
+
});
|
|
296
|
+
// deno-lint-ignore no-explicit-any
|
|
297
|
+
this.pathways[path] = true;
|
|
298
|
+
this.beforeObservable[path] = new rxjs_1.Subject();
|
|
299
|
+
this.afterObservers[path] = new rxjs_1.Subject();
|
|
300
|
+
this.errorObservers[path] = new rxjs_1.Subject();
|
|
301
|
+
if (writable) {
|
|
302
|
+
if (contract.isFilePathway) {
|
|
303
|
+
this.filePathways.add(path);
|
|
304
|
+
this.writers[path] = this.webhookBuilderFactory()
|
|
305
|
+
.buildFileWebhook(contract.flowType, contract.eventType).send;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
this.writers[path] = this.webhookBuilderFactory()
|
|
309
|
+
.buildWebhook(contract.flowType, contract.eventType).send;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (contract.timeoutMs) {
|
|
313
|
+
this.timeouts[path] = contract.timeoutMs;
|
|
314
|
+
}
|
|
315
|
+
if (contract.maxRetries !== undefined) {
|
|
316
|
+
this.maxRetries[path] = contract.maxRetries;
|
|
317
|
+
}
|
|
318
|
+
if (contract.retryDelayMs !== undefined) {
|
|
319
|
+
this.retryDelays[path] = contract.retryDelayMs;
|
|
320
|
+
}
|
|
321
|
+
this.schemas[path] = contract.schema;
|
|
322
|
+
this.writable[path] = writable;
|
|
323
|
+
this.logger.info(`Pathway registered successfully`, {
|
|
324
|
+
pathway: path,
|
|
325
|
+
flowType: contract.flowType,
|
|
326
|
+
eventType: contract.eventType,
|
|
327
|
+
writable
|
|
328
|
+
});
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
get(path) {
|
|
332
|
+
this.logger.debug(`Getting pathway`, { pathway: String(path) });
|
|
333
|
+
return this.pathways[path];
|
|
334
|
+
}
|
|
335
|
+
handle(path, handler) {
|
|
336
|
+
const pathStr = String(path);
|
|
337
|
+
this.logger.debug(`Setting handler for pathway`, { pathway: pathStr });
|
|
338
|
+
const pathway = this.pathways[path];
|
|
339
|
+
if (!pathway) {
|
|
340
|
+
const error = `Pathway ${pathStr} not found`;
|
|
341
|
+
this.logger.error(error);
|
|
342
|
+
throw new Error(error);
|
|
343
|
+
}
|
|
344
|
+
if (this.handlers[path]) {
|
|
345
|
+
const error = `Someone is already handling pathway ${pathStr} in this instance`;
|
|
346
|
+
this.logger.error(error);
|
|
347
|
+
throw new Error(error);
|
|
348
|
+
}
|
|
349
|
+
this.handlers[path] = handler;
|
|
350
|
+
this.logger.info(`Handler set for pathway`, { pathway: pathStr });
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Subscribe to pathway events (before or after processing)
|
|
354
|
+
* @param path The pathway to subscribe to
|
|
355
|
+
* @param handler The handler function for the events
|
|
356
|
+
* @param type The event type to subscribe to (before, after, or all)
|
|
357
|
+
*/
|
|
358
|
+
subscribe(path, handler, type = "before") {
|
|
359
|
+
const pathStr = String(path);
|
|
360
|
+
this.logger.debug(`Subscribing to pathway events`, {
|
|
361
|
+
pathway: pathStr,
|
|
362
|
+
type
|
|
363
|
+
});
|
|
364
|
+
if (!this.pathways[path]) {
|
|
365
|
+
const error = `Pathway ${pathStr} not found`;
|
|
366
|
+
this.logger.error(error);
|
|
367
|
+
throw new Error(error);
|
|
368
|
+
}
|
|
369
|
+
if (type === "before") {
|
|
370
|
+
this.beforeObservable[path].subscribe(handler);
|
|
371
|
+
}
|
|
372
|
+
else if (type === "after") {
|
|
373
|
+
this.afterObservers[path].subscribe(handler);
|
|
374
|
+
}
|
|
375
|
+
else if (type === "all") {
|
|
376
|
+
// Subscribe to both before and after events
|
|
377
|
+
this.beforeObservable[path].subscribe(handler);
|
|
378
|
+
this.afterObservers[path].subscribe(handler);
|
|
379
|
+
}
|
|
380
|
+
this.logger.debug(`Subscribed to pathway events`, {
|
|
381
|
+
pathway: pathStr,
|
|
382
|
+
type
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Subscribe to errors for a specific pathway
|
|
387
|
+
* @param path The pathway to subscribe to errors for
|
|
388
|
+
* @param handler The handler function that receives the error and event
|
|
389
|
+
*/
|
|
390
|
+
onError(path, handler) {
|
|
391
|
+
const pathStr = String(path);
|
|
392
|
+
this.logger.debug(`Subscribing to pathway errors`, { pathway: pathStr });
|
|
393
|
+
if (!this.pathways[path]) {
|
|
394
|
+
const error = `Pathway ${pathStr} not found`;
|
|
395
|
+
this.logger.error(error);
|
|
396
|
+
throw new Error(error);
|
|
397
|
+
}
|
|
398
|
+
this.errorObservers[path].subscribe(({ event, error }) => handler(error, event));
|
|
399
|
+
this.logger.debug(`Subscribed to pathway errors`, { pathway: pathStr });
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Subscribe to errors for all pathways
|
|
403
|
+
* @param handler The handler function that receives the error, event, and pathway name
|
|
404
|
+
*/
|
|
405
|
+
onAnyError(handler) {
|
|
406
|
+
this.logger.debug(`Subscribing to all pathway errors`);
|
|
407
|
+
this.globalErrorSubject.subscribe(({ pathway, event, error }) => handler(error, event, pathway));
|
|
408
|
+
this.logger.debug(`Subscribed to all pathway errors`);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Writes data to a pathway with optional audit metadata
|
|
412
|
+
* @param path The pathway to write to
|
|
413
|
+
* @param data The data to write
|
|
414
|
+
* @param metadata Optional metadata to include with the event
|
|
415
|
+
* @param options Optional write options
|
|
416
|
+
* @returns A promise that resolves to the event ID(s)
|
|
417
|
+
*/
|
|
418
|
+
async write(path, data, metadata, options) {
|
|
419
|
+
const pathStr = String(path);
|
|
420
|
+
this.logger.debug(`Writing to pathway`, {
|
|
421
|
+
pathway: pathStr,
|
|
422
|
+
metadata,
|
|
423
|
+
options: {
|
|
424
|
+
fireAndForget: options?.fireAndForget
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
if (!this.pathways[path]) {
|
|
428
|
+
const error = `Pathway ${pathStr} not found`;
|
|
429
|
+
this.logger.error(error);
|
|
430
|
+
throw new Error(error);
|
|
431
|
+
}
|
|
432
|
+
if (!this.writable[path]) {
|
|
433
|
+
const error = `Pathway ${pathStr} is not writable`;
|
|
434
|
+
this.logger.error(error);
|
|
435
|
+
throw new Error(error);
|
|
436
|
+
}
|
|
437
|
+
const schema = this.schemas[path];
|
|
438
|
+
if (!value_1.Value.Check(schema, data)) {
|
|
439
|
+
const error = `Invalid data for pathway ${pathStr}`;
|
|
440
|
+
this.logger.error(error, {
|
|
441
|
+
pathway: pathStr,
|
|
442
|
+
schema: schema.toString()
|
|
443
|
+
});
|
|
444
|
+
throw new Error(error);
|
|
445
|
+
}
|
|
446
|
+
// Create a copy of the metadata to avoid modifying the original
|
|
447
|
+
const finalMetadata = metadata ? { ...metadata } : {};
|
|
448
|
+
// Process audit metadata if audit is configured
|
|
449
|
+
if (this.userIdResolver) {
|
|
450
|
+
this.logger.debug(`Resolving user ID for audit metadata`, { pathway: pathStr });
|
|
451
|
+
const userId = await this.userIdResolver();
|
|
452
|
+
// Determine the audit mode: default is "user" unless explicitly specified as "system"
|
|
453
|
+
const auditMode = finalMetadata?.["audit/mode"] || "user";
|
|
454
|
+
this.logger.debug(`Adding audit metadata`, {
|
|
455
|
+
pathway: pathStr,
|
|
456
|
+
auditMode,
|
|
457
|
+
userId
|
|
458
|
+
});
|
|
459
|
+
// Add appropriate audit metadata based on mode
|
|
460
|
+
if (auditMode === "system") {
|
|
461
|
+
finalMetadata["audit/user-id"] = "system";
|
|
462
|
+
finalMetadata["audit/on-behalf-of"] = userId;
|
|
463
|
+
finalMetadata["audit/mode"] = "system";
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
finalMetadata["audit/user-id"] = userId;
|
|
467
|
+
finalMetadata["audit/mode"] = "user"; // Always set mode for user
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
let eventIds = [];
|
|
471
|
+
if (this.filePathways.has(path)) {
|
|
472
|
+
this.logger.debug(`Writing file data to pathway`, { pathway: pathStr });
|
|
473
|
+
const fileData = data;
|
|
474
|
+
eventIds = await this.writers[path](fileData, finalMetadata, options);
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
this.logger.debug(`Writing webhook data to pathway`, { pathway: pathStr });
|
|
478
|
+
eventIds = await this.writers[path](data, finalMetadata, options);
|
|
479
|
+
}
|
|
480
|
+
this.logger.info(`Successfully wrote to pathway`, {
|
|
481
|
+
pathway: pathStr,
|
|
482
|
+
eventIds: Array.isArray(eventIds) ? eventIds : [eventIds],
|
|
483
|
+
fireAndForget: options?.fireAndForget
|
|
484
|
+
});
|
|
485
|
+
if (!options?.fireAndForget) {
|
|
486
|
+
this.logger.debug(`Waiting for pathway to be processed`, {
|
|
487
|
+
pathway: pathStr,
|
|
488
|
+
eventIds: Array.isArray(eventIds) ? eventIds : [eventIds]
|
|
489
|
+
});
|
|
490
|
+
await Promise.all(Array.isArray(eventIds)
|
|
491
|
+
? eventIds.map(id => this.waitForPathwayToBeProcessed(id))
|
|
492
|
+
: [this.waitForPathwayToBeProcessed(eventIds)]);
|
|
493
|
+
}
|
|
494
|
+
return eventIds;
|
|
495
|
+
}
|
|
496
|
+
async waitForPathwayToBeProcessed(eventId) {
|
|
497
|
+
const startTime = Date.now();
|
|
498
|
+
const timeoutMs = this.timeouts[eventId] ?? this.pathwayTimeoutMs;
|
|
499
|
+
this.logger.debug(`Waiting for event to be processed`, {
|
|
500
|
+
eventId,
|
|
501
|
+
timeoutMs
|
|
502
|
+
});
|
|
503
|
+
let attempts = 0;
|
|
504
|
+
while (!(await this.pathwayState.isProcessed(eventId))) {
|
|
505
|
+
attempts++;
|
|
506
|
+
const elapsedTime = Date.now() - startTime;
|
|
507
|
+
if (elapsedTime > timeoutMs) {
|
|
508
|
+
const error = `Pathway processing timed out after ${timeoutMs}ms for event ${eventId}`;
|
|
509
|
+
this.logger.error(error, {
|
|
510
|
+
eventId,
|
|
511
|
+
timeoutMs,
|
|
512
|
+
elapsedTime,
|
|
513
|
+
attempts
|
|
514
|
+
});
|
|
515
|
+
throw new Error(error);
|
|
516
|
+
}
|
|
517
|
+
if (attempts % 10 === 0) { // Log every 10 attempts (1 second)
|
|
518
|
+
this.logger.debug(`Still waiting for event to be processed`, {
|
|
519
|
+
eventId,
|
|
520
|
+
elapsedTime,
|
|
521
|
+
attempts,
|
|
522
|
+
timeoutMs
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
526
|
+
}
|
|
527
|
+
this.logger.debug(`Event has been processed`, {
|
|
528
|
+
eventId,
|
|
529
|
+
elapsedTime: Date.now() - startTime,
|
|
530
|
+
attempts
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
exports.PathwaysBuilder = PathwaysBuilder;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main export module for the pathways functionality
|
|
3
|
+
*
|
|
4
|
+
* Exports all components needed to build and manage pathways including:
|
|
5
|
+
* - Pathway builder
|
|
6
|
+
* - State management
|
|
7
|
+
* - Storage adapters (KV, Postgres)
|
|
8
|
+
* - Logging
|
|
9
|
+
* - Type definitions
|
|
10
|
+
*/
|
|
11
|
+
export * from "./builder.js";
|
|
12
|
+
export * from "./internal-pathway.state.js";
|
|
13
|
+
export * from "./kv/kv-adapter.js";
|
|
14
|
+
export * from "./logger.js";
|
|
15
|
+
export * from "./postgres/index.js";
|
|
16
|
+
export * from "./types.js";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pathways/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* Main export module for the pathways functionality
|
|
19
|
+
*
|
|
20
|
+
* Exports all components needed to build and manage pathways including:
|
|
21
|
+
* - Pathway builder
|
|
22
|
+
* - State management
|
|
23
|
+
* - Storage adapters (KV, Postgres)
|
|
24
|
+
* - Logging
|
|
25
|
+
* - Type definitions
|
|
26
|
+
*/
|
|
27
|
+
__exportStar(require("./builder.js"), exports);
|
|
28
|
+
__exportStar(require("./internal-pathway.state.js"), exports);
|
|
29
|
+
__exportStar(require("./kv/kv-adapter.js"), exports);
|
|
30
|
+
__exportStar(require("./logger.js"), exports);
|
|
31
|
+
__exportStar(require("./postgres/index.js"), exports);
|
|
32
|
+
__exportStar(require("./types.js"), exports);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PathwayState } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Internal implementation of PathwayState interface that uses KV storage
|
|
4
|
+
* for tracking processed events to prevent duplicate processing
|
|
5
|
+
*
|
|
6
|
+
* @implements {PathwayState}
|
|
7
|
+
*/
|
|
8
|
+
export declare class InternalPathwayState implements PathwayState {
|
|
9
|
+
/**
|
|
10
|
+
* Default time-to-live for processed event records (5 minutes)
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
private static readonly DEFAULT_TTL_MS;
|
|
14
|
+
/**
|
|
15
|
+
* The KV adapter instance for storage
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
private kv;
|
|
19
|
+
/**
|
|
20
|
+
* Gets or initializes the KV adapter
|
|
21
|
+
*
|
|
22
|
+
* @private
|
|
23
|
+
* @returns {Promise<KvAdapter>} The KV adapter instance
|
|
24
|
+
*/
|
|
25
|
+
private getKv;
|
|
26
|
+
/**
|
|
27
|
+
* Checks if an event has already been processed
|
|
28
|
+
*
|
|
29
|
+
* @param {string} eventId - The ID of the event to check
|
|
30
|
+
* @returns {Promise<boolean>} True if the event has been processed, false otherwise
|
|
31
|
+
*/
|
|
32
|
+
isProcessed(eventId: string): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Marks an event as processed
|
|
35
|
+
*
|
|
36
|
+
* @param {string} eventId - The ID of the event to mark as processed
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
setProcessed(eventId: string): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=internal-pathway.state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-pathway.state.d.ts","sourceRoot":"","sources":["../../src/pathways/internal-pathway.state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAEvD;;;OAGG;IACH,OAAO,CAAC,EAAE,CAA0B;IAEpC;;;;;OAKG;YACW,KAAK;IAOnB;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMpD;;;;;OAKG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAInD"}
|