@shipfox/api-triggers 12.1.0 → 12.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +33 -0
- package/dist/core/entities/decision.d.ts +1 -1
- package/dist/core/entities/decision.d.ts.map +1 -1
- package/dist/core/entities/received-event.d.ts +2 -2
- package/dist/core/entities/received-event.d.ts.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/route-event-to-job-listeners.d.ts.map +1 -1
- package/dist/core/route-event-to-job-listeners.js +130 -13
- package/dist/core/route-event-to-job-listeners.js.map +1 -1
- package/dist/core/workflows-client.d.ts +12 -12
- package/dist/db/db.d.ts +20 -20
- package/dist/db/job-listener-subscriptions.d.ts +1 -0
- package/dist/db/job-listener-subscriptions.d.ts.map +1 -1
- package/dist/db/job-listener-subscriptions.js +3 -0
- package/dist/db/job-listener-subscriptions.js.map +1 -1
- package/dist/db/schema/cron-schedules.d.ts +1 -1
- package/dist/db/schema/decisions.d.ts +3 -3
- package/dist/db/schema/job-listener-subscriptions.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/received-events.d.ts +5 -5
- package/dist/db/schema/subscriptions.d.ts +1 -1
- package/dist/metrics/instance.d.ts +2 -2
- package/dist/presentation/subscribers/on-integration-event-received.d.ts.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +11 -11
- package/src/core/route-event-to-job-listeners.test.ts +97 -0
- package/src/core/route-event-to-job-listeners.ts +157 -11
- package/src/db/job-listener-subscriptions.test.ts +3 -0
- package/src/db/job-listener-subscriptions.ts +4 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-triggers",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "12.
|
|
4
|
+
"version": "12.3.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -22,23 +22,23 @@
|
|
|
22
22
|
"cron-parser": "^5.6.1",
|
|
23
23
|
"drizzle-orm": "^0.45.2",
|
|
24
24
|
"zod": "^4.4.3",
|
|
25
|
-
"@shipfox/api-auth-context": "12.
|
|
26
|
-
"@shipfox/api-definitions-dto": "12.
|
|
27
|
-
"@shipfox/api-integration-core-dto": "12.
|
|
25
|
+
"@shipfox/api-auth-context": "12.2.0",
|
|
26
|
+
"@shipfox/api-definitions-dto": "12.3.0",
|
|
27
|
+
"@shipfox/api-integration-core-dto": "12.2.0",
|
|
28
28
|
"@shipfox/api-triggers-dto": "9.0.2",
|
|
29
|
-
"@shipfox/api-workflows-dto": "12.
|
|
29
|
+
"@shipfox/api-workflows-dto": "12.3.0",
|
|
30
30
|
"@shipfox/config": "1.2.4",
|
|
31
|
-
"@shipfox/expression": "2.
|
|
31
|
+
"@shipfox/expression": "2.2.0",
|
|
32
32
|
"@shipfox/inter-module": "0.2.3",
|
|
33
33
|
"@shipfox/node-drizzle": "0.3.5",
|
|
34
34
|
"@shipfox/node-error-monitoring": "0.3.0",
|
|
35
|
-
"@shipfox/node-fastify": "0.4.
|
|
36
|
-
"@shipfox/node-module": "1.0.
|
|
37
|
-
"@shipfox/node-opentelemetry": "0.6.
|
|
35
|
+
"@shipfox/node-fastify": "0.4.2",
|
|
36
|
+
"@shipfox/node-module": "1.0.6",
|
|
37
|
+
"@shipfox/node-opentelemetry": "0.6.4",
|
|
38
38
|
"@shipfox/node-outbox": "0.2.6",
|
|
39
39
|
"@shipfox/node-postgres": "0.5.0",
|
|
40
|
-
"@shipfox/node-temporal": "0.4.
|
|
41
|
-
"@shipfox/workflow-document": "3.0.
|
|
40
|
+
"@shipfox/node-temporal": "0.4.5",
|
|
41
|
+
"@shipfox/workflow-document": "3.0.1"
|
|
42
42
|
},
|
|
43
43
|
"imports": {
|
|
44
44
|
"#*": "./dist/*"
|
|
@@ -319,6 +319,103 @@ describe('routeEventToJobListeners', () => {
|
|
|
319
319
|
expect(result).toMatchObject({engagedCount: 1, matchedJobCount: 1, acceptedJobCount: 1});
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
+
it.each([
|
|
323
|
+
{
|
|
324
|
+
name: 'integer output',
|
|
325
|
+
filter: 'jobs.build.outputs.count + 1 == 43',
|
|
326
|
+
filterSnapshot: {jobs: {build: {outputs: {count: 42}}}},
|
|
327
|
+
filterOutputTypes: {build: {count: 'int'}},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'timestamp output',
|
|
331
|
+
filter: 'jobs.build.outputs.createdAt < timestamp("2026-07-01T00:00:00Z")',
|
|
332
|
+
filterSnapshot: {jobs: {build: {outputs: {createdAt: '2026-06-30T12:00:00.000Z'}}}},
|
|
333
|
+
filterOutputTypes: {build: {createdAt: 'timestamp'}},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'integer execution output',
|
|
337
|
+
filter: 'jobs.build.executions[0].outputs.count + 1 == 43',
|
|
338
|
+
filterSnapshot: {jobs: {build: {executions: [{outputs: {count: 42}}]}}},
|
|
339
|
+
filterOutputTypes: {build: {count: 'int'}},
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'timestamp execution output',
|
|
343
|
+
filter: 'jobs.build.executions[0].outputs.createdAt < timestamp("2026-07-01T00:00:00Z")',
|
|
344
|
+
filterSnapshot: {
|
|
345
|
+
jobs: {build: {executions: [{outputs: {createdAt: '2026-06-30T12:00:00.000Z'}}]}},
|
|
346
|
+
},
|
|
347
|
+
filterOutputTypes: {build: {createdAt: 'timestamp'}},
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: 'nested output',
|
|
351
|
+
filter: 'jobs.build.outputs.details.count + 1 == 43',
|
|
352
|
+
filterSnapshot: {jobs: {build: {outputs: {details: {count: 42}}}}},
|
|
353
|
+
filterOutputTypes: {
|
|
354
|
+
build: {details: {kind: 'object', fields: {count: 'int'}}},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
])('rehydrates a $name in listener filters', async ({
|
|
358
|
+
filter,
|
|
359
|
+
filterSnapshot,
|
|
360
|
+
filterOutputTypes,
|
|
361
|
+
}) => {
|
|
362
|
+
const workspaceId = crypto.randomUUID();
|
|
363
|
+
await jobListenerSubscriptionFactory.create({
|
|
364
|
+
workspaceId,
|
|
365
|
+
source: 'github',
|
|
366
|
+
event: 'pull_request_review',
|
|
367
|
+
config: {
|
|
368
|
+
filter,
|
|
369
|
+
filter_snapshot: filterSnapshot,
|
|
370
|
+
filter_output_types: filterOutputTypes,
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const result = await route({workspaceId});
|
|
375
|
+
|
|
376
|
+
expect(deliverEventToListener).toHaveBeenCalledTimes(1);
|
|
377
|
+
expect(listenerFilterErrored).not.toHaveBeenCalled();
|
|
378
|
+
expect(result).toMatchObject({engagedCount: 1, matchedJobCount: 1, acceptedJobCount: 1});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it.each([
|
|
382
|
+
'on',
|
|
383
|
+
'until',
|
|
384
|
+
] as const)('rehydrates CEL scalar values in %s listener filter snapshots', async (kind) => {
|
|
385
|
+
const workspaceId = crypto.randomUUID();
|
|
386
|
+
await jobListenerSubscriptionFactory.create({
|
|
387
|
+
workspaceId,
|
|
388
|
+
kind,
|
|
389
|
+
source: 'github',
|
|
390
|
+
event: 'pull_request_review',
|
|
391
|
+
config: {
|
|
392
|
+
filter:
|
|
393
|
+
'run.number + 1 == 2 && run.created_at < timestamp("2026-07-01T00:00:00Z") && jobs.build.executions[0].index + 1 == 1 && jobs.build.executions[0].started_at < timestamp("2026-07-01T00:00:00Z") && jobs.build.executions[0].events[0].received_at < timestamp("2026-07-01T00:00:00Z")',
|
|
394
|
+
filter_snapshot: {
|
|
395
|
+
run: {number: 1, created_at: '2026-06-30T12:00:00.000Z'},
|
|
396
|
+
jobs: {
|
|
397
|
+
build: {
|
|
398
|
+
executions: [
|
|
399
|
+
{
|
|
400
|
+
index: 0,
|
|
401
|
+
started_at: '2026-06-30T12:00:00.000Z',
|
|
402
|
+
finished_at: null,
|
|
403
|
+
events: [{received_at: '2026-06-30T12:00:00.000Z'}],
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const result = await route({workspaceId});
|
|
413
|
+
|
|
414
|
+
expect(deliverEventToListener).toHaveBeenCalledTimes(1);
|
|
415
|
+
expect(listenerFilterErrored).not.toHaveBeenCalled();
|
|
416
|
+
expect(result).toMatchObject({engagedCount: 1, matchedJobCount: 1, acceptedJobCount: 1});
|
|
417
|
+
});
|
|
418
|
+
|
|
322
419
|
it('does not deliver or record a decision when a listener filter returns false', async () => {
|
|
323
420
|
const workspaceId = crypto.randomUUID();
|
|
324
421
|
await jobListenerSubscriptionFactory.create({
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ListenerFilterOutputTypes,
|
|
3
|
+
listenerFilterOutputTypesSchema,
|
|
4
|
+
} from '@shipfox/api-workflows-dto';
|
|
5
|
+
import {projectWorkflowPredicateContext, rehydrateJsonExpressionRecord} from '@shipfox/expression';
|
|
2
6
|
import {logger} from '@shipfox/node-opentelemetry';
|
|
3
7
|
import {findMatchingJobListenerSubscriptions} from '#db/job-listener-subscriptions.js';
|
|
4
8
|
import {evaluateStoredFilter, type StoredFilterEvaluation} from './config.js';
|
|
@@ -9,6 +13,8 @@ import {
|
|
|
9
13
|
type WorkflowsModuleClient,
|
|
10
14
|
} from './workflows-client.js';
|
|
11
15
|
|
|
16
|
+
const LISTENER_INTEGER_PATTERN = /^-?\d+$/;
|
|
17
|
+
|
|
12
18
|
export interface RouteEventToJobListenersParams {
|
|
13
19
|
workflows: WorkflowsModuleClient;
|
|
14
20
|
history: TriggerHistoryRecorder;
|
|
@@ -158,7 +164,10 @@ function evaluateListenerFilter(params: EvaluateListenerFilterParams): StoredFil
|
|
|
158
164
|
value: filter,
|
|
159
165
|
context: projectWorkflowPredicateContext(
|
|
160
166
|
params.subscription.kind === 'on' ? 'listener.on' : 'listener.until',
|
|
161
|
-
{
|
|
167
|
+
{
|
|
168
|
+
...rehydrateListenerFilterSnapshot(snapshot.value, snapshot.outputTypes),
|
|
169
|
+
event: params.payload,
|
|
170
|
+
},
|
|
162
171
|
),
|
|
163
172
|
invalidReason: 'Listener subscription filter must be a non-empty string when set',
|
|
164
173
|
evaluationFailedReason: 'Listener filter evaluation failed',
|
|
@@ -168,22 +177,159 @@ function evaluateListenerFilter(params: EvaluateListenerFilterParams): StoredFil
|
|
|
168
177
|
function readFilterSnapshot(
|
|
169
178
|
subscription: JobListenerSubscription,
|
|
170
179
|
):
|
|
171
|
-
| {kind: 'valid'; value: Record<string, unknown
|
|
180
|
+
| {kind: 'valid'; value: Record<string, unknown>; outputTypes?: ListenerFilterOutputTypes}
|
|
172
181
|
| {kind: 'invalid'; result: Extract<StoredFilterEvaluation, {kind: 'filter-error'}>} {
|
|
173
182
|
const value = subscription.config.filter_snapshot;
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
if (
|
|
184
|
+
value !== undefined &&
|
|
185
|
+
(value === null || typeof value !== 'object' || Array.isArray(value))
|
|
186
|
+
) {
|
|
187
|
+
return {
|
|
188
|
+
kind: 'invalid',
|
|
189
|
+
result: {
|
|
190
|
+
kind: 'filter-error',
|
|
191
|
+
reason: 'Listener filter snapshot must be an object when set',
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const outputTypesValue = subscription.config.filter_output_types;
|
|
197
|
+
if (outputTypesValue !== undefined) {
|
|
198
|
+
const parsedOutputTypes = listenerFilterOutputTypesSchema.safeParse(outputTypesValue);
|
|
199
|
+
if (!parsedOutputTypes.success) {
|
|
200
|
+
return {
|
|
201
|
+
kind: 'invalid',
|
|
202
|
+
result: {
|
|
203
|
+
kind: 'filter-error',
|
|
204
|
+
reason: 'Listener filter output types must be valid when set',
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
kind: 'valid',
|
|
210
|
+
value: (value ?? {}) as Record<string, unknown>,
|
|
211
|
+
outputTypes: parsedOutputTypes.data,
|
|
212
|
+
};
|
|
177
213
|
}
|
|
214
|
+
|
|
215
|
+
return {kind: 'valid', value: (value ?? {}) as Record<string, unknown>};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function rehydrateListenerFilterSnapshot(
|
|
219
|
+
snapshot: Record<string, unknown>,
|
|
220
|
+
outputTypes: ListenerFilterOutputTypes | undefined,
|
|
221
|
+
): Record<string, unknown> {
|
|
222
|
+
let result = snapshot;
|
|
223
|
+
if (outputTypes !== undefined && isRecord(snapshot.jobs)) {
|
|
224
|
+
const jobs = Object.fromEntries(
|
|
225
|
+
Object.entries(snapshot.jobs).map(([jobKey, jobContext]) => {
|
|
226
|
+
const jobOutputTypes = Object.hasOwn(outputTypes, jobKey) ? outputTypes[jobKey] : undefined;
|
|
227
|
+
if (jobOutputTypes === undefined || !isRecord(jobContext)) {
|
|
228
|
+
return [jobKey, jobContext];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const outputs = isRecord(jobContext.outputs)
|
|
232
|
+
? rehydrateJsonExpressionRecord(jobContext.outputs, jobOutputTypes)
|
|
233
|
+
: jobContext.outputs;
|
|
234
|
+
const executions = Array.isArray(jobContext.executions)
|
|
235
|
+
? jobContext.executions.map((execution) => {
|
|
236
|
+
if (!isRecord(execution) || !isRecord(execution.outputs)) return execution;
|
|
237
|
+
return {
|
|
238
|
+
...execution,
|
|
239
|
+
outputs: rehydrateJsonExpressionRecord(execution.outputs, jobOutputTypes),
|
|
240
|
+
};
|
|
241
|
+
})
|
|
242
|
+
: jobContext.executions;
|
|
243
|
+
|
|
244
|
+
return [
|
|
245
|
+
jobKey,
|
|
246
|
+
{
|
|
247
|
+
...jobContext,
|
|
248
|
+
outputs,
|
|
249
|
+
executions,
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
result = {...snapshot, jobs};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const rehydrated = {...result};
|
|
258
|
+
if (isRecord(result.run)) rehydrated.run = rehydrateRunSnapshot(result.run);
|
|
259
|
+
if (isRecord(result.jobs)) rehydrated.jobs = rehydrateJobsSnapshot(result.jobs);
|
|
260
|
+
return rehydrated;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
264
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function rehydrateRunSnapshot(run: Record<string, unknown>): Record<string, unknown> {
|
|
268
|
+
const result = {...run};
|
|
269
|
+
if ('number' in run) result.number = rehydrateListenerInt(run.number);
|
|
270
|
+
if ('created_at' in run) result.created_at = rehydrateListenerTimestamp(run.created_at);
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function rehydrateJobsSnapshot(jobs: Record<string, unknown>): Record<string, unknown> {
|
|
275
|
+
return Object.fromEntries(
|
|
276
|
+
Object.entries(jobs).map(([jobKey, value]) => [
|
|
277
|
+
jobKey,
|
|
278
|
+
isRecord(value) ? rehydrateJobSnapshot(value) : value,
|
|
279
|
+
]),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function rehydrateJobSnapshot(job: Record<string, unknown>): Record<string, unknown> {
|
|
284
|
+
if (!Array.isArray(job.executions)) return job;
|
|
285
|
+
|
|
178
286
|
return {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
},
|
|
287
|
+
...job,
|
|
288
|
+
executions: job.executions.map((value) =>
|
|
289
|
+
isRecord(value) ? rehydrateExecutionSnapshot(value) : value,
|
|
290
|
+
),
|
|
184
291
|
};
|
|
185
292
|
}
|
|
186
293
|
|
|
294
|
+
function rehydrateExecutionSnapshot(execution: Record<string, unknown>): Record<string, unknown> {
|
|
295
|
+
const result = {...execution};
|
|
296
|
+
if ('index' in execution) result.index = rehydrateListenerInt(execution.index);
|
|
297
|
+
if ('started_at' in execution) {
|
|
298
|
+
result.started_at = rehydrateListenerTimestamp(execution.started_at);
|
|
299
|
+
}
|
|
300
|
+
if ('finished_at' in execution) {
|
|
301
|
+
result.finished_at = rehydrateListenerTimestamp(execution.finished_at);
|
|
302
|
+
}
|
|
303
|
+
if (Array.isArray(execution.events)) {
|
|
304
|
+
result.events = execution.events.map((value) =>
|
|
305
|
+
isRecord(value) ? rehydrateExecutionEventSnapshot(value) : value,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function rehydrateExecutionEventSnapshot(event: Record<string, unknown>): Record<string, unknown> {
|
|
312
|
+
if (!('received_at' in event)) return event;
|
|
313
|
+
return {...event, received_at: rehydrateListenerTimestamp(event.received_at)};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function rehydrateListenerInt(value: unknown): unknown {
|
|
317
|
+
if (typeof value === 'number' && Number.isSafeInteger(value)) return BigInt(value);
|
|
318
|
+
if (typeof value !== 'string' || !LISTENER_INTEGER_PATTERN.test(value)) return value;
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
return BigInt(value);
|
|
322
|
+
} catch {
|
|
323
|
+
return value;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function rehydrateListenerTimestamp(value: unknown): unknown {
|
|
328
|
+
if (typeof value !== 'string') return value;
|
|
329
|
+
const date = new Date(value);
|
|
330
|
+
return Number.isNaN(date.getTime()) ? value : date;
|
|
331
|
+
}
|
|
332
|
+
|
|
187
333
|
function listenerDisposition(subscription: JobListenerSubscription): 'fire' | 'resolve' {
|
|
188
334
|
return subscription.kind === 'until' ? 'resolve' : 'fire';
|
|
189
335
|
}
|
|
@@ -123,6 +123,7 @@ describe('job listener subscriptions', () => {
|
|
|
123
123
|
|
|
124
124
|
it('persists listener filter snapshots verbatim in matcher config', async () => {
|
|
125
125
|
const filterSnapshot = {jobs: {build: {outputs: {pr_number: 42}}}};
|
|
126
|
+
const filterOutputTypes = {build: {pr_number: 'int' as const}};
|
|
126
127
|
const payload = buildActivatedPayload({
|
|
127
128
|
on: [
|
|
128
129
|
{
|
|
@@ -130,6 +131,7 @@ describe('job listener subscriptions', () => {
|
|
|
130
131
|
event: 'pull_request_review',
|
|
131
132
|
filter: 'jobs.build.outputs.pr_number == event.pull_request.number',
|
|
132
133
|
filter_snapshot: filterSnapshot,
|
|
134
|
+
filter_output_types: filterOutputTypes,
|
|
133
135
|
},
|
|
134
136
|
],
|
|
135
137
|
until: null,
|
|
@@ -142,6 +144,7 @@ describe('job listener subscriptions', () => {
|
|
|
142
144
|
expect(rows[0]?.config).toEqual({
|
|
143
145
|
filter: 'jobs.build.outputs.pr_number == event.pull_request.number',
|
|
144
146
|
filter_snapshot: filterSnapshot,
|
|
147
|
+
filter_output_types: filterOutputTypes,
|
|
145
148
|
});
|
|
146
149
|
});
|
|
147
150
|
|
|
@@ -17,6 +17,7 @@ export interface ListenerMatcher {
|
|
|
17
17
|
inputs?: Readonly<Record<string, unknown>> | undefined;
|
|
18
18
|
filter?: string | undefined;
|
|
19
19
|
filter_snapshot?: Readonly<Record<string, unknown>> | undefined;
|
|
20
|
+
filter_output_types?: Readonly<Record<string, unknown>> | undefined;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface ProjectJobListenerSubscriptionsParams {
|
|
@@ -43,6 +44,9 @@ export async function projectJobListenerSubscriptions(
|
|
|
43
44
|
if (matcher.inputs !== undefined) config.inputs = matcher.inputs;
|
|
44
45
|
if (matcher.filter !== undefined) config.filter = matcher.filter;
|
|
45
46
|
if (matcher.filter_snapshot !== undefined) config.filter_snapshot = matcher.filter_snapshot;
|
|
47
|
+
if (matcher.filter_output_types !== undefined) {
|
|
48
|
+
config.filter_output_types = matcher.filter_output_types;
|
|
49
|
+
}
|
|
46
50
|
|
|
47
51
|
await tx
|
|
48
52
|
.insert(jobListenerSubscriptions)
|