@reventlessdev/reventless-local 3.0.0-alpha.288 → 3.0.0-alpha.290

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 (30) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +10 -10
  3. package/src/adapter/Webhook/LocalWebhookRegistry.res +64 -0
  4. package/src/adapter/Webhook/LocalWebhookRegistry.res.mjs +52 -0
  5. package/src/components/InboundTranslationSlice_Builder.res +10 -1
  6. package/src/components/InboundTranslationSlice_Builder.res.mjs +9 -1
  7. package/tests/adapter/InboundTranslationMutationTest.res.mjs +6 -4
  8. package/tests/components/automationslice/AutomationRescheduleFixtures.res +0 -1
  9. package/tests/components/automationslice/AutomationRescheduleFixtures.res.mjs +0 -1
  10. package/tests/components/automationslice/AutomationScheduleFixtures.res +0 -1
  11. package/tests/components/automationslice/AutomationScheduleFixtures.res.mjs +0 -5
  12. package/tests/components/automationslice/AutomationSliceFixtures.res +0 -2
  13. package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +0 -2
  14. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +0 -1
  15. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +0 -1
  16. package/tests/components/automationslice/AutomationSweepFixtures.res +0 -1
  17. package/tests/components/automationslice/AutomationSweepFixtures.res.mjs +0 -1
  18. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +0 -1
  19. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +0 -1
  20. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +2 -0
  21. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +25 -20
  22. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +3 -0
  23. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +3 -0
  24. package/tests/components/inboundtranslationslice/InboundTranslationWebhookTest.res +305 -0
  25. package/tests/components/inboundtranslationslice/InboundTranslationWebhookTest.res.mjs +473 -0
  26. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +0 -2
  27. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +0 -2
  28. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +0 -2
  29. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +0 -1
  30. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res.mjs +0 -1
@@ -0,0 +1,473 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Sury from "sury";
4
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
5
+ import * as Nodecrypto from "node:crypto";
6
+ import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
7
+ import * as Pulumi from "@pulumi/pulumi";
8
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
+ import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
10
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
11
+ import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
12
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
13
+ import * as LocalWebhookRegistry$ReventlessLocal from "../../../src/adapter/Webhook/LocalWebhookRegistry.res.mjs";
14
+ import * as InboundTranslationSlice_Webhook$ReventlessCore from "@reventlessdev/reventless-core/src/components/InboundTranslationSlice/InboundTranslationSlice_Webhook.res.mjs";
15
+ import * as InboundTranslationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs";
16
+
17
+ TestRunner$ReventlessLocal.setup();
18
+
19
+ let merchandiser = "Merchandiser";
20
+
21
+ let moduleUrl = "signed-feed://spec";
22
+
23
+ let externalInputSchema = Sury.$schema(s => ({
24
+ kind: s.m(Sury.string),
25
+ id: s.m(Sury.string)
26
+ }));
27
+
28
+ let commandSchema = Sury.union([
29
+ Sury.$schema(s => ({
30
+ TAG: "AddProduct",
31
+ productId: s.m(DcbTag$Reventless.string)
32
+ })),
33
+ Sury.$schema(s => ({
34
+ TAG: "AddCategory",
35
+ categoryId: s.m(DcbTag$Reventless.string)
36
+ }))
37
+ ]);
38
+
39
+ function authorizationOf(name) {
40
+ if (name === "AddProduct") {
41
+ return {
42
+ TAG: "AllowRoles",
43
+ _0: [merchandiser]
44
+ };
45
+ } else {
46
+ return {
47
+ TAG: "AllowRoles",
48
+ _0: [Role$Reventless.admin]
49
+ };
50
+ }
51
+ }
52
+
53
+ let endpoint = {
54
+ path: "/feeds/signed/",
55
+ auth: {
56
+ TAG: "SignedBody",
57
+ _0: {
58
+ header: "X-Signature",
59
+ prefix: "sha256=",
60
+ timestampHeader: "X-Timestamp",
61
+ secret: "feed"
62
+ }
63
+ },
64
+ role: merchandiser
65
+ };
66
+
67
+ let traits = [];
68
+
69
+ let FeedSpec = {
70
+ name: "SignedFeed",
71
+ moduleUrl: moduleUrl,
72
+ externalInputSchema: externalInputSchema,
73
+ commandSchema: commandSchema,
74
+ targetName: "AddProduct",
75
+ externalSystem: undefined,
76
+ authorizationOf: authorizationOf,
77
+ endpoint: endpoint,
78
+ traits: traits
79
+ };
80
+
81
+ function translateFeed(input) {
82
+ let kind = input.kind;
83
+ switch (kind) {
84
+ case "category" :
85
+ return {
86
+ TAG: "Ok",
87
+ _0: [[
88
+ input.id,
89
+ {
90
+ TAG: "AddCategory",
91
+ categoryId: input.id
92
+ }
93
+ ]]
94
+ };
95
+ case "product" :
96
+ return {
97
+ TAG: "Ok",
98
+ _0: [[
99
+ input.id,
100
+ {
101
+ TAG: "AddProduct",
102
+ productId: input.id
103
+ }
104
+ ]]
105
+ };
106
+ default:
107
+ return {
108
+ TAG: "Error",
109
+ _0: `unknown kind ` + kind
110
+ };
111
+ }
112
+ }
113
+
114
+ let FeedCallback = InboundTranslationSlice_Callback$ReventlessCore.Make(FeedSpec)({
115
+ translate: translateFeed,
116
+ moduleUrl: moduleUrl
117
+ });
118
+
119
+ let moduleUrl$1 = "open-feed://spec";
120
+
121
+ let externalInputSchema$1 = Sury.$schema(s => ({
122
+ kind: s.m(Sury.string),
123
+ id: s.m(Sury.string)
124
+ }));
125
+
126
+ let commandSchema$1 = Sury.$schema(s => ({
127
+ TAG: "AddProduct",
128
+ productId: s.m(DcbTag$Reventless.string)
129
+ }));
130
+
131
+ function authorizationOf$1(param) {
132
+ return "AllowAuthenticated";
133
+ }
134
+
135
+ let traits$1 = [];
136
+
137
+ let OpenSpec_endpoint = {
138
+ path: "open",
139
+ auth: "Open"
140
+ };
141
+
142
+ let OpenSpec = {
143
+ name: "OpenFeed",
144
+ moduleUrl: moduleUrl$1,
145
+ externalInputSchema: externalInputSchema$1,
146
+ commandSchema: commandSchema$1,
147
+ targetName: "AddProduct",
148
+ externalSystem: undefined,
149
+ authorizationOf: authorizationOf$1,
150
+ endpoint: OpenSpec_endpoint,
151
+ traits: traits$1
152
+ };
153
+
154
+ function translate(input) {
155
+ return {
156
+ TAG: "Ok",
157
+ _0: [[
158
+ input.id,
159
+ {
160
+ TAG: "AddProduct",
161
+ productId: input.id
162
+ }
163
+ ]]
164
+ };
165
+ }
166
+
167
+ let OpenCallback = InboundTranslationSlice_Callback$ReventlessCore.Make(OpenSpec)({
168
+ translate: translate,
169
+ moduleUrl: moduleUrl$1
170
+ });
171
+
172
+ let inputSchema = Sury.$schema(s => ({
173
+ id: s.m(Sury.string)
174
+ }));
175
+
176
+ let published = {
177
+ contents: []
178
+ };
179
+
180
+ async function publishJsons(cmds) {
181
+ published.contents = published.contents.concat(cmds);
182
+ }
183
+
184
+ function feedOps_receive(input, caller, door) {
185
+ return FeedCallback.receive(publishJsons, input, caller, door);
186
+ }
187
+
188
+ async function feedOps_refuse(body, errorCode, error) {
189
+ return FeedCallback.refuse(body, errorCode, error);
190
+ }
191
+
192
+ let feedOps = {
193
+ receive: feedOps_receive,
194
+ refuse: feedOps_refuse
195
+ };
196
+
197
+ function openOps_receive(input, caller, door) {
198
+ return OpenCallback.receive(publishJsons, input, caller, door);
199
+ }
200
+
201
+ async function openOps_refuse(body, errorCode, error) {
202
+ return OpenCallback.refuse(body, errorCode, error);
203
+ }
204
+
205
+ let openOps = {
206
+ receive: openOps_receive,
207
+ refuse: openOps_refuse
208
+ };
209
+
210
+ let viaWebhook = "Webhook";
211
+
212
+ let viaMutation = "Mutation";
213
+
214
+ let secret = "s3cret";
215
+
216
+ function secretOf(name) {
217
+ if (name === "feed") {
218
+ return secret;
219
+ }
220
+ }
221
+
222
+ let signature = {
223
+ header: "X-Signature",
224
+ prefix: "sha256=",
225
+ timestampHeader: "X-Timestamp",
226
+ secret: "feed"
227
+ };
228
+
229
+ function hmac(secret, text) {
230
+ return Nodecrypto.createHmac("sha256", secret).update(text).digest("hex");
231
+ }
232
+
233
+ function signedHeaders(secretOpt, atOpt, body) {
234
+ let secret$1 = secretOpt !== undefined ? secretOpt : secret;
235
+ let at = atOpt !== undefined ? atOpt : 1700000000;
236
+ let at$1 = at.toString();
237
+ return Object.fromEntries([
238
+ [
239
+ "x-timestamp",
240
+ at$1
241
+ ],
242
+ [
243
+ "x-signature",
244
+ "sha256=" + hmac(secret$1, at$1 + `.` + body)
245
+ ]
246
+ ]);
247
+ }
248
+
249
+ function body(kindOpt, idOpt) {
250
+ let kind = kindOpt !== undefined ? kindOpt : "product";
251
+ let id = idOpt !== undefined ? idOpt : "p-1";
252
+ return `{"kind":"` + kind + `","id":"` + id + `"}`;
253
+ }
254
+
255
+ function post(pathOpt, method, headers, body) {
256
+ let path = pathOpt !== undefined ? pathOpt : "feeds/signed";
257
+ return LocalWebhookRegistry$ReventlessLocal.post(path, method, headers, secretOf, 1700000000, body);
258
+ }
259
+
260
+ function throws(run) {
261
+ try {
262
+ run();
263
+ return false;
264
+ } catch (exn) {
265
+ return true;
266
+ }
267
+ }
268
+
269
+ function rows(log) {
270
+ return Object.values(log);
271
+ }
272
+
273
+ function clear(log) {
274
+ Object.keys(log).forEach(key => Stdlib_Dict.$$delete(log, key));
275
+ }
276
+
277
+ globalThis.describe("webhook signature", () => {
278
+ let check = (secretsOpt, headers, text) => {
279
+ let secrets = secretsOpt !== undefined ? secretsOpt : [secret];
280
+ return InboundTranslationSlice_Webhook$ReventlessCore.verify(signature, secrets, headers, text, 1700000000);
281
+ };
282
+ globalThis.test("a signature by the shared secret passes", () => {
283
+ globalThis.expect(check(undefined, signedHeaders(undefined, undefined, body(undefined, undefined)), body(undefined, undefined))).toEqual({
284
+ TAG: "Ok",
285
+ _0: undefined
286
+ });
287
+ });
288
+ globalThis.test("a signature by another secret is refused", () => {
289
+ globalThis.expect(Stdlib_Result.isError(check(undefined, signedHeaders("other", undefined, body(undefined, undefined)), body(undefined, undefined)))).toBe(true);
290
+ });
291
+ globalThis.test("a body changed after signing is refused", () => {
292
+ globalThis.expect(Stdlib_Result.isError(check(undefined, signedHeaders(undefined, undefined, body(undefined, undefined)), body(undefined, "p-2")))).toBe(true);
293
+ });
294
+ globalThis.test("a timestamp outside the window is refused, so a captured request cannot be replayed", () => {
295
+ globalThis.expect(Stdlib_Result.isError(check(undefined, signedHeaders(undefined, 1700000000 - 301, body(undefined, undefined)), body(undefined, undefined)))).toBe(true);
296
+ });
297
+ globalThis.test("a request without the signature header is refused", () => {
298
+ let headers = signedHeaders(undefined, undefined, body(undefined, undefined));
299
+ Stdlib_Dict.$$delete(headers, "x-signature");
300
+ globalThis.expect(Stdlib_Result.isError(check(undefined, headers, body(undefined, undefined)))).toBe(true);
301
+ });
302
+ globalThis.test("while a secret is rotated, either one passes", () => {
303
+ globalThis.expect(check(InboundTranslationSlice_Webhook$ReventlessCore.secretsOf("new, old"), signedHeaders("old", undefined, body(undefined, undefined)), body(undefined, undefined))).toEqual({
304
+ TAG: "Ok",
305
+ _0: undefined
306
+ });
307
+ });
308
+ globalThis.test("a secret is read from a variable named after it", () => {
309
+ globalThis.expect(InboundTranslationSlice_Webhook$ReventlessCore.secretEnvKey("supplier-feed")).toBe("REVENTLESS_WEBHOOK_SECRET_SUPPLIER_FEED");
310
+ });
311
+ });
312
+
313
+ globalThis.describe("webhook door", () => {
314
+ globalThis.beforeAll(() => {
315
+ LocalWebhookRegistry$ReventlessLocal.reset();
316
+ let match = FeedCallback.webhook;
317
+ let match$1 = OpenCallback.webhook;
318
+ if (match !== undefined && match$1 !== undefined) {
319
+ LocalWebhookRegistry$ReventlessLocal.register(match, Pulumi.output(feedOps));
320
+ return LocalWebhookRegistry$ReventlessLocal.register(match$1, Pulumi.output(openOps));
321
+ } else {
322
+ return Stdlib_JsError.throwWithMessage("fixture specs declare endpoints");
323
+ }
324
+ });
325
+ globalThis.afterAll(() => LocalWebhookRegistry$ReventlessLocal.reset());
326
+ globalThis.beforeEach(() => {
327
+ published.contents = [];
328
+ clear(FeedCallback.auditLog);
329
+ clear(OpenCallback.auditLog);
330
+ });
331
+ globalThis.test("a signed request publishes its commands as the endpoint's role", async () => {
332
+ let response = await post(undefined, undefined, signedHeaders(undefined, undefined, body(undefined, undefined)), body(undefined, undefined));
333
+ globalThis.expect(response.status).toBe(200);
334
+ globalThis.expect(published.contents.map(c => c.id)).toEqual(["p-1"]);
335
+ globalThis.expect(Object.values(FeedCallback.auditLog).map(row => row.door)).toEqual([viaWebhook]);
336
+ });
337
+ globalThis.test("a command the endpoint's role may not send is refused", async () => {
338
+ let text = body("category", undefined);
339
+ let response = await post(undefined, undefined, signedHeaders(undefined, undefined, text), text);
340
+ globalThis.expect(response.status).toBe(403);
341
+ globalThis.expect(published.contents).toEqual([]);
342
+ });
343
+ globalThis.test("a bad signature is refused before the body is read, with an audit row", async () => {
344
+ let response = await post(undefined, undefined, signedHeaders("other", undefined, body(undefined, undefined)), body(undefined, undefined));
345
+ globalThis.expect(response.status).toBe(401);
346
+ globalThis.expect(published.contents).toEqual([]);
347
+ globalThis.expect(Object.values(FeedCallback.auditLog).map(row => [
348
+ row.status,
349
+ row.door,
350
+ row.input
351
+ ])).toEqual([[
352
+ "Failure",
353
+ viaWebhook,
354
+ body(undefined, undefined)
355
+ ]]);
356
+ });
357
+ globalThis.test("a signed body that is not JSON is refused", async () => {
358
+ let response = await post(undefined, undefined, signedHeaders(undefined, undefined, "not json"), "not json");
359
+ globalThis.expect(response.status).toBe(400);
360
+ });
361
+ globalThis.test("an unknown path and a wrong method are refused", async () => {
362
+ let unknown = await post("nowhere", undefined, undefined, body(undefined, undefined));
363
+ let wrongMethod = await post(undefined, "PUT", signedHeaders(undefined, undefined, body(undefined, undefined)), body(undefined, undefined));
364
+ globalThis.expect([
365
+ unknown.status,
366
+ wrongMethod.status
367
+ ]).toEqual([
368
+ 404,
369
+ 405
370
+ ]);
371
+ });
372
+ globalThis.test("a secret the platform lacks fails closed, without an audit row", async () => {
373
+ let response = await LocalWebhookRegistry$ReventlessLocal.post("feeds/signed", undefined, signedHeaders(undefined, undefined, body(undefined, undefined)), param => {}, 1700000000, body(undefined, undefined));
374
+ globalThis.expect(response.status).toBe(500);
375
+ globalThis.expect(published.contents).toEqual([]);
376
+ globalThis.expect(Object.values(FeedCallback.auditLog)).toEqual([]);
377
+ });
378
+ globalThis.test("an open URL identifies nobody, so a signed-in rule refuses it", async () => {
379
+ let response = await post("open", undefined, undefined, body(undefined, undefined));
380
+ globalThis.expect(response.status).toBe(403);
381
+ });
382
+ globalThis.test("both doors publish the same commands and are told apart in the audit", async () => {
383
+ let caller_groups = [merchandiser];
384
+ let caller = {
385
+ userId: "u-1",
386
+ username: "alice",
387
+ groups: caller_groups,
388
+ provider: "InMemory"
389
+ };
390
+ let mutationResult = await feedOps_receive(JSON.parse(body(undefined, undefined)), caller, undefined);
391
+ await post(undefined, undefined, signedHeaders(undefined, undefined, body(undefined, undefined)), body(undefined, undefined));
392
+ globalThis.expect(Stdlib_Result.isOk(mutationResult)).toBe(true);
393
+ let commands = published.contents.map(c => c.commandJson);
394
+ if (commands.length !== 2) {
395
+ globalThis.expect(commands.length).toBe(2);
396
+ } else {
397
+ let viaMutationCommand = commands[0];
398
+ let viaWebhookCommand = commands[1];
399
+ globalThis.expect(viaWebhookCommand).toEqual(viaMutationCommand);
400
+ }
401
+ globalThis.expect(Object.values(FeedCallback.auditLog).map(row => row.door)).toEqual([
402
+ viaMutation,
403
+ viaWebhook
404
+ ]);
405
+ });
406
+ globalThis.test("two slices may not share a path", () => {
407
+ let route = FeedCallback.webhook;
408
+ if (route !== undefined) {
409
+ globalThis.expect(throws(() => LocalWebhookRegistry$ReventlessLocal.register(route, Pulumi.output(feedOps)))).toBe(true);
410
+ return;
411
+ } else {
412
+ return Stdlib_JsError.throwWithMessage("fixture spec declares an endpoint");
413
+ }
414
+ });
415
+ });
416
+
417
+ globalThis.describe("which doors a slice has", () => {
418
+ globalThis.test("@noApi on externalInput withholds the mutation", () => {
419
+ globalThis.expect([
420
+ InboundTranslationSlice_Callback$ReventlessCore.exposesMutation(Api$ReventlessInfra.markNoApi(inputSchema)),
421
+ InboundTranslationSlice_Callback$ReventlessCore.exposesMutation(inputSchema)
422
+ ]).toEqual([
423
+ false,
424
+ true
425
+ ]);
426
+ });
427
+ globalThis.test("a slice with neither door is refused", () => {
428
+ let check = webhook => throws(() => InboundTranslationSlice_Callback$ReventlessCore.checkReachable("P", "S", false, webhook));
429
+ globalThis.expect([
430
+ check(false),
431
+ check(true)
432
+ ]).toEqual([
433
+ true,
434
+ false
435
+ ]);
436
+ });
437
+ });
438
+
439
+ let Webhook;
440
+
441
+ let Callback;
442
+
443
+ let now = 1700000000;
444
+
445
+ export {
446
+ Webhook,
447
+ Callback,
448
+ merchandiser,
449
+ FeedSpec,
450
+ translateFeed,
451
+ FeedCallback,
452
+ OpenSpec,
453
+ OpenCallback,
454
+ inputSchema,
455
+ published,
456
+ publishJsons,
457
+ feedOps,
458
+ openOps,
459
+ viaWebhook,
460
+ viaMutation,
461
+ secret,
462
+ now,
463
+ secretOf,
464
+ signature,
465
+ hmac,
466
+ signedHeaders,
467
+ body,
468
+ post,
469
+ throws,
470
+ rows,
471
+ clear,
472
+ }
473
+ /* Not a pure module */
@@ -44,7 +44,6 @@ let FireForgetCallback = OutboundTranslationSlice_Callback$ReventlessCore.Make({
44
44
  outboundItemSchema: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.outboundItemSchema,
45
45
  inboundCommandSchema: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.inboundCommandSchema,
46
46
  maxRetries: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.maxRetries,
47
- heartbeatInterval: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.heartbeatInterval,
48
47
  targetName: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.targetName,
49
48
  sourceNames: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.sourceNames,
50
49
  externalSystem: OutboundTranslationSliceFixtures$ReventlessLocal.SendTrackingEmailSpec.externalSystem,
@@ -59,7 +58,6 @@ let CommandBackCallback = OutboundTranslationSlice_Callback$ReventlessCore.Make(
59
58
  outboundItemSchema: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.outboundItemSchema,
60
59
  inboundCommandSchema: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.inboundCommandSchema,
61
60
  maxRetries: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.maxRetries,
62
- heartbeatInterval: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.heartbeatInterval,
63
61
  targetName: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.targetName,
64
62
  sourceNames: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.sourceNames,
65
63
  externalSystem: OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.externalSystem,
@@ -38,7 +38,6 @@ module SendTrackingEmailSpec = {
38
38
  let onExhausted = (_id, _item, ~lastError as _) => None
39
39
 
40
40
  let maxRetries = 3
41
- let heartbeatInterval = 60
42
41
  let targetName = None
43
42
  let sourceNames: array<string> = []
44
43
  let externalSystem = None
@@ -86,7 +85,6 @@ module ProcessPaymentSpec = {
86
85
  let onExhausted = (id, item, ~lastError) => onExhaustedFn.contents(id, item, lastError)
87
86
 
88
87
  let maxRetries = 2
89
- let heartbeatInterval = 30
90
88
  let targetName = Some("ConfirmPayment")
91
89
  let sourceNames: array<string> = []
92
90
  let externalSystem = None
@@ -76,7 +76,6 @@ let SendTrackingEmailSpec = {
76
76
  translate: translate,
77
77
  onExhausted: onExhausted,
78
78
  maxRetries: 3,
79
- heartbeatInterval: 60,
80
79
  targetName: undefined,
81
80
  sourceNames: sourceNames,
82
81
  externalSystem: undefined,
@@ -158,7 +157,6 @@ let ProcessPaymentSpec = {
158
157
  onExhaustedFn: onExhaustedFn,
159
158
  onExhausted: onExhausted$1,
160
159
  maxRetries: 2,
161
- heartbeatInterval: 30,
162
160
  targetName: ProcessPaymentSpec_targetName,
163
161
  sourceNames: sourceNames$1,
164
162
  externalSystem: undefined,
@@ -78,7 +78,6 @@ module SendConfirmSpec = {
78
78
  type inboundCommand = unit
79
79
 
80
80
  let maxRetries = 3
81
- let heartbeatInterval = 60
82
81
  let targetName = None
83
82
  let sourceNames: array<string> = []
84
83
  let externalSystem = Some("EmailService")
@@ -121,7 +121,6 @@ let SendConfirmSpec = {
121
121
  outboundItemSchema: outboundItemSchema,
122
122
  inboundCommandSchema: inboundCommandSchema,
123
123
  maxRetries: 3,
124
- heartbeatInterval: 60,
125
124
  targetName: undefined,
126
125
  sourceNames: sourceNames,
127
126
  externalSystem: SendConfirmSpec_externalSystem,