@trayio/cdk-runtime 5.21.0 → 5.22.0-unstable

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 (21) hide show
  1. package/dist/connector/operation/CompositeOperationExecution.d.ts +6 -1
  2. package/dist/connector/operation/CompositeOperationExecution.d.ts.map +1 -1
  3. package/dist/connector/operation/CompositeOperationExecution.js +45 -8
  4. package/dist/connector/operation/CompositeOperationExecution.unit.test.d.ts +2 -0
  5. package/dist/connector/operation/CompositeOperationExecution.unit.test.d.ts.map +1 -0
  6. package/dist/connector/operation/CompositeOperationExecution.unit.test.js +276 -0
  7. package/dist/connector/operation/CompositeOperationExecutionFactory.d.ts +4 -2
  8. package/dist/connector/operation/CompositeOperationExecutionFactory.d.ts.map +1 -1
  9. package/dist/connector/operation/CompositeOperationExecutionFactory.js +5 -3
  10. package/dist/connector/operation/CompositeOperationExecutionPolling.d.ts +33 -0
  11. package/dist/connector/operation/CompositeOperationExecutionPolling.d.ts.map +1 -0
  12. package/dist/connector/operation/CompositeOperationExecutionPolling.js +142 -0
  13. package/dist/connector/operation/OperationExecutionFactory.d.ts.map +1 -1
  14. package/dist/connector/operation/OperationExecutionFactory.js +3 -2
  15. package/dist/connector/polling/PollingServiceClient.d.ts +50 -0
  16. package/dist/connector/polling/PollingServiceClient.d.ts.map +1 -0
  17. package/dist/connector/polling/PollingServiceClient.js +128 -0
  18. package/dist/connector/polling/PollingServiceClient.unit.test.d.ts +2 -0
  19. package/dist/connector/polling/PollingServiceClient.unit.test.d.ts.map +1 -0
  20. package/dist/connector/polling/PollingServiceClient.unit.test.js +218 -0
  21. package/package.json +7 -6
@@ -2,12 +2,17 @@ import { OperationHandlerAuth, OperationHandlerContext, OperationHandlerResult,
2
2
  import { CompositeOperationHandler } from '@trayio/cdk-dsl/connector/operation/CompositeOperationHandler';
3
3
  import AbstractOperationExecution from './AbstractOperationExecution';
4
4
  import { OperationHandlerInvocationFactory } from './OperationHandlerInvocationFactory';
5
+ import { PollingServiceClient } from '../polling/PollingServiceClient';
5
6
  export declare class CompositeOperationExecution<AUTH extends OperationHandlerAuth<unknown, unknown>, IN, OUT> extends AbstractOperationExecution<AUTH, IN, OUT> {
6
7
  private handlerInvocationFactory;
7
8
  private handler;
8
9
  handlerType: OperationHandlerType;
9
- constructor(handlerInvocationFactory: OperationHandlerInvocationFactory, handler: CompositeOperationHandler<AUTH, IN, OUT>, handlerType: OperationHandlerType);
10
+ private operationName;
11
+ private pollingServiceClient;
12
+ constructor(handlerInvocationFactory: OperationHandlerInvocationFactory, handler: CompositeOperationHandler<AUTH, IN, OUT>, handlerType: OperationHandlerType, operationName: string, pollingServiceClient: PollingServiceClient);
10
13
  execute(ctx: OperationHandlerContext<AUTH>, input: IN): Promise<OperationHandlerResult<OUT>>;
14
+ private executePollingLifecycle;
15
+ private mapCaughtError;
11
16
  private isError;
12
17
  }
13
18
  //# sourceMappingURL=CompositeOperationExecution.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CompositeOperationExecution.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecution.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EAEtB,oBAAoB,EACpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAC;AAE1G,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAExF,qBAAa,2BAA2B,CACvC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EACnD,EAAE,EACF,GAAG,CACF,SAAQ,0BAA0B,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;IAClD,OAAO,CAAC,wBAAwB,CAAoC;IACpE,OAAO,CAAC,OAAO,CAA2C;IAC1D,WAAW,EAAE,oBAAoB,CAAC;gBAGjC,wBAAwB,EAAE,iCAAiC,EAC3D,OAAO,EAAE,yBAAyB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EACjD,WAAW,EAAE,oBAAoB;IAQ5B,OAAO,CACZ,GAAG,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAClC,KAAK,EAAE,EAAE,GACP,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAgCvC,OAAO,CAAC,OAAO;CAQf"}
1
+ {"version":3,"file":"CompositeOperationExecution.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecution.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,uBAAuB,EAEvB,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAC;AAE1G,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAQvE,qBAAa,2BAA2B,CACvC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EACnD,EAAE,EACF,GAAG,CACF,SAAQ,0BAA0B,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;IAClD,OAAO,CAAC,wBAAwB,CAAoC;IACpE,OAAO,CAAC,OAAO,CAA2C;IAC1D,WAAW,EAAE,oBAAoB,CAAC;IAClC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAAuB;gBAGlD,wBAAwB,EAAE,iCAAiC,EAC3D,OAAO,EAAE,yBAAyB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EACjD,WAAW,EAAE,oBAAoB,EACjC,aAAa,EAAE,MAAM,EACrB,oBAAoB,EAAE,oBAAoB;IAUrC,OAAO,CACZ,GAAG,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAClC,KAAK,EAAE,EAAE,GACP,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAgCzB,uBAAuB;IAuBrC,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,OAAO;CAQf"}
@@ -6,17 +6,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CompositeOperationExecution = void 0;
7
7
  const OperationHandler_1 = require("@trayio/cdk-dsl/connector/operation/OperationHandler");
8
8
  const AbstractOperationExecution_1 = __importDefault(require("./AbstractOperationExecution"));
9
+ const CompositeOperationExecutionPolling_1 = require("./CompositeOperationExecutionPolling");
9
10
  class CompositeOperationExecution extends AbstractOperationExecution_1.default {
10
11
  handlerInvocationFactory;
11
12
  handler;
12
13
  handlerType;
13
- constructor(handlerInvocationFactory, handler, handlerType) {
14
+ operationName;
15
+ pollingServiceClient;
16
+ constructor(handlerInvocationFactory, handler, handlerType, operationName, pollingServiceClient) {
14
17
  super();
15
18
  this.handlerInvocationFactory = handlerInvocationFactory;
16
19
  this.handler = handler;
17
20
  this.handlerType = handlerType;
21
+ this.operationName = operationName;
22
+ this.pollingServiceClient = pollingServiceClient;
18
23
  }
19
24
  async execute(ctx, input) {
25
+ if ((0, CompositeOperationExecutionPolling_1.isPollingLifecycle)(this.handlerType)) {
26
+ return this.executePollingLifecycle(ctx, input);
27
+ }
28
+ if (this.handlerType === OperationHandler_1.OperationHandlerType.TriggerPollDedup) {
29
+ return (0, CompositeOperationExecutionPolling_1.runTriggerPollDedup)({
30
+ ctx,
31
+ input,
32
+ handler: this.handler,
33
+ handlerInvocationFactory: this.handlerInvocationFactory,
34
+ validateOperationResponse: (r) => this.validateOperationResponse(r),
35
+ mapCaughtError: (e) => this.mapCaughtError(e),
36
+ });
37
+ }
20
38
  const handlerInvocation = this.handlerInvocationFactory(ctx);
21
39
  try {
22
40
  const result = await this.handler.handlerFunction(ctx, input, handlerInvocation);
@@ -24,14 +42,33 @@ class CompositeOperationExecution extends AbstractOperationExecution_1.default {
24
42
  return result;
25
43
  }
26
44
  catch (error) {
27
- if (this.isError(error)) {
28
- return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError(error.message));
29
- }
30
- if (typeof error === 'string') {
31
- return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError(error));
32
- }
33
- return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError('Operation failed with unknown error'));
45
+ return this.mapCaughtError(error);
46
+ }
47
+ }
48
+ async executePollingLifecycle(ctx, input) {
49
+ try {
50
+ const pollingInput = (input ?? {});
51
+ await (0, CompositeOperationExecutionPolling_1.runPollingRegisterOrDeregister)({
52
+ ctx,
53
+ handlerType: this.handlerType,
54
+ operationName: this.operationName,
55
+ pollingInput,
56
+ pollingServiceClient: this.pollingServiceClient,
57
+ });
58
+ return OperationHandler_1.OperationHandlerResult.success({});
59
+ }
60
+ catch (error) {
61
+ return this.mapCaughtError(error);
62
+ }
63
+ }
64
+ mapCaughtError(error) {
65
+ if (this.isError(error)) {
66
+ return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError(error.message));
67
+ }
68
+ if (typeof error === 'string') {
69
+ return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError(error));
34
70
  }
71
+ return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.connectorError('Operation failed with unknown error'));
35
72
  }
36
73
  // TODO: Move to commons
37
74
  isError(error) {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CompositeOperationExecution.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompositeOperationExecution.unit.test.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecution.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const OperationHandler_1 = require("@trayio/cdk-dsl/connector/operation/OperationHandler");
4
+ const CompositeOperationHandler_1 = require("@trayio/cdk-dsl/connector/operation/CompositeOperationHandler");
5
+ const PollingTypes_1 = require("@trayio/cdk-dsl/connector/operation/PollingTypes");
6
+ const CompositeOperationExecution_1 = require("./CompositeOperationExecution");
7
+ const noopHandlerInvocationFactory = () => (() => undefined);
8
+ const fakePollingContext = () => ({
9
+ auth: {
10
+ authType: 'TOKEN',
11
+ authId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
12
+ user: {},
13
+ app: {},
14
+ },
15
+ connectorName: 'salesforce',
16
+ connectorVersion: '7.2.0',
17
+ executionToken: 'exec-jwt',
18
+ publicUrl: 'https://trigger.example.test/tab-123',
19
+ organizationId: 'org-uuid',
20
+ workspaceId: 'ws-uuid',
21
+ projectId: '7fa00000-1111-2222-3333-444455556666',
22
+ });
23
+ const createFakePollingServiceClient = () => {
24
+ const registerCalls = [];
25
+ const deregisterCalls = [];
26
+ const client = {
27
+ register: jest.fn((payload, token) => {
28
+ registerCalls.push([payload, token]);
29
+ return Promise.resolve();
30
+ }),
31
+ deregister: jest.fn((payload, token) => {
32
+ deregisterCalls.push([payload, token]);
33
+ return Promise.resolve();
34
+ }),
35
+ };
36
+ return { client, registerCalls, deregisterCalls };
37
+ };
38
+ describe('CompositeOperationExecution polling lifecycle branch', () => {
39
+ it('calls PollingServiceClient.register with ctx + input fields on TriggerPollCreate, never invoking handlerFunction', async () => {
40
+ const { client, registerCalls } = createFakePollingServiceClient();
41
+ const handlerFunction = jest.fn();
42
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
43
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollCreate, 'new_records_poll', client);
44
+ const input = {
45
+ pollingInterval: { value: 15, unit: PollingTypes_1.PollingIntervalUnit.Minutes },
46
+ maxFailureCount: 50,
47
+ initialCursor: { since: 't0' },
48
+ };
49
+ const result = await execution.execute(fakePollingContext(), input);
50
+ expect(result.isSuccess).toBe(true);
51
+ expect(handlerFunction).not.toHaveBeenCalled();
52
+ expect(registerCalls).toHaveLength(1);
53
+ const [payload, token] = registerCalls[0];
54
+ expect(payload).toEqual({
55
+ connectorName: 'salesforce',
56
+ connectorVersion: '7.2.0',
57
+ registeredHandlerName: 'new_records_poll',
58
+ authenticationId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
59
+ organizationId: 'org-uuid',
60
+ workspaceId: 'ws-uuid',
61
+ projectId: '7fa00000-1111-2222-3333-444455556666',
62
+ publicUrl: 'https://trigger.example.test/tab-123',
63
+ pollingInterval: {
64
+ value: 15,
65
+ unit: PollingTypes_1.PollingIntervalUnit.Minutes,
66
+ },
67
+ maxFailureCount: 50,
68
+ initialCursor: { since: 't0' },
69
+ });
70
+ expect(token).toBe('exec-jwt');
71
+ });
72
+ it('calls PollingServiceClient.deregister on TriggerPollDestroy, never invoking handlerFunction', async () => {
73
+ const { client, deregisterCalls } = createFakePollingServiceClient();
74
+ const handlerFunction = jest.fn();
75
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
76
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDestroy, 'new_records_poll_destroy', client);
77
+ const result = await execution.execute(fakePollingContext(), {});
78
+ expect(result.isSuccess).toBe(true);
79
+ expect(handlerFunction).not.toHaveBeenCalled();
80
+ expect(deregisterCalls).toHaveLength(1);
81
+ const [payload, token] = deregisterCalls[0];
82
+ expect(payload).toEqual({
83
+ connectorName: 'salesforce',
84
+ connectorVersion: '7.2.0',
85
+ registeredHandlerName: 'new_records_poll_destroy',
86
+ publicUrl: 'https://trigger.example.test/tab-123',
87
+ preserveCursor: true,
88
+ });
89
+ expect(token).toBe('exec-jwt');
90
+ });
91
+ it('surfaces a consolidated failure listing every missing ctx field on register', async () => {
92
+ const { client, registerCalls } = createFakePollingServiceClient();
93
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn());
94
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollCreate, 'new_records_poll', client);
95
+ const result = await execution.execute({}, {});
96
+ expect(registerCalls).toHaveLength(0);
97
+ expect(result.isFailure).toBe(true);
98
+ const error = result._tag === 'Failure' ? result.error.message : '';
99
+ expect(error).toMatch(/ctx\.connectorName/);
100
+ expect(error).toMatch(/ctx\.connectorVersion/);
101
+ expect(error).toMatch(/ctx\.executionToken/);
102
+ expect(error).toMatch(/ctx\.publicUrl/);
103
+ });
104
+ it('fails register when the auth/org/workspace/project ctx fields are missing', async () => {
105
+ const { client, registerCalls } = createFakePollingServiceClient();
106
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn());
107
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollCreate, 'new_records_poll', client);
108
+ // Base polling ctx is present, but the identity fields the PS register
109
+ // payload requires are not.
110
+ const incompleteCtx = {
111
+ connectorName: 'x',
112
+ connectorVersion: '1',
113
+ executionToken: 'tok',
114
+ publicUrl: 'u',
115
+ };
116
+ const result = await execution.execute(incompleteCtx, {});
117
+ expect(registerCalls).toHaveLength(0);
118
+ expect(result.isFailure).toBe(true);
119
+ const error = result._tag === 'Failure' ? result.error.message : '';
120
+ expect(error).toMatch(/ctx\.auth\.authId/);
121
+ expect(error).toMatch(/ctx\.organizationId/);
122
+ expect(error).toMatch(/ctx\.workspaceId/);
123
+ expect(error).toMatch(/ctx\.projectId/);
124
+ });
125
+ it('deregister does NOT require the register-only ctx fields', async () => {
126
+ const { client, deregisterCalls } = createFakePollingServiceClient();
127
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn());
128
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDestroy, 'new_records_poll_destroy', client);
129
+ // Only the base four ctx fields; auth / org / workspace / project absent.
130
+ const destroyOnlyCtx = {
131
+ connectorName: 'x',
132
+ connectorVersion: '1',
133
+ executionToken: 'tok',
134
+ publicUrl: 'u',
135
+ };
136
+ const result = await execution.execute(destroyOnlyCtx, {});
137
+ expect(result.isSuccess).toBe(true);
138
+ expect(deregisterCalls).toHaveLength(1);
139
+ });
140
+ it('maps a PollingServiceClient error into an OperationHandlerResult failure', async () => {
141
+ const client = {
142
+ register: jest.fn(() => Promise.reject(new Error('boom'))),
143
+ deregister: jest.fn(),
144
+ };
145
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn());
146
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollCreate, 'new_records_poll', client);
147
+ const result = await execution.execute(fakePollingContext(), {});
148
+ expect(result.isFailure).toBe(true);
149
+ const message = result._tag === 'Failure' ? result.error.message : '';
150
+ expect(message).toBe('boom');
151
+ });
152
+ });
153
+ describe('CompositeOperationExecution polling dedup branch', () => {
154
+ it('short-circuits on a Polling Service failure envelope with a UserInputError before the dev body runs', async () => {
155
+ const { client } = createFakePollingServiceClient();
156
+ const handlerFunction = jest.fn();
157
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
158
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
159
+ const input = {
160
+ input: { tableName: 't' },
161
+ event: { error: 'upstream_gave_up', message: 'boom' },
162
+ };
163
+ const result = await execution.execute({}, input);
164
+ expect(handlerFunction).not.toHaveBeenCalled();
165
+ expect(result.isFailure).toBe(true);
166
+ const failure = result._tag === 'Failure' ? result.error : null;
167
+ expect(failure._tag).toBe('UserInputError');
168
+ expect(failure.message).toBe('boom');
169
+ });
170
+ it('stringifies a non-string message on the failure envelope for the UserInputError body', async () => {
171
+ const { client } = createFakePollingServiceClient();
172
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn());
173
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
174
+ const input = {
175
+ input: {},
176
+ event: {
177
+ error: 'upstream_gave_up',
178
+ message: { code: 404, detail: 'nope' },
179
+ },
180
+ };
181
+ const result = await execution.execute({}, input);
182
+ expect(result.isFailure).toBe(true);
183
+ const message = result._tag === 'Failure' ? result.error.message : '';
184
+ expect(message).toBe(JSON.stringify({ code: 404, detail: 'nope' }));
185
+ });
186
+ it("lifts the dev's triggerDeduplicationId from body to metadata while preserving the body on success", async () => {
187
+ const { client } = createFakePollingServiceClient();
188
+ const handlerFunction = jest
189
+ .fn()
190
+ .mockResolvedValue(OperationHandler_1.OperationHandlerResult.success({ triggerDeduplicationId: 'evt-42' }));
191
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
192
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
193
+ const input = {
194
+ input: { tableName: 't' },
195
+ event: { id: 'evt-42', payload: { foo: 'bar' } },
196
+ };
197
+ const result = await execution.execute({}, input);
198
+ expect(handlerFunction).toHaveBeenCalledTimes(1);
199
+ expect(result.isSuccess).toBe(true);
200
+ expect(result._tag === 'Success' ? result.value : null).toEqual({
201
+ triggerDeduplicationId: 'evt-42',
202
+ });
203
+ expect(result.metadata).toEqual({ triggerDeduplicationId: 'evt-42' });
204
+ });
205
+ it('merges the lifted triggerDeduplicationId with metadata the dev already set (e.g. usage)', async () => {
206
+ const { client } = createFakePollingServiceClient();
207
+ const handlerFunction = jest
208
+ .fn()
209
+ .mockResolvedValue(OperationHandler_1.OperationHandlerResult.success({ triggerDeduplicationId: 'evt-9' }, { usage: [{ unitType: 'calls', consumed: 1 }] }));
210
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
211
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
212
+ const result = await execution.execute({}, {
213
+ input: {},
214
+ event: { id: 'evt-9' },
215
+ });
216
+ expect(result.isSuccess).toBe(true);
217
+ expect(result.metadata).toEqual({
218
+ usage: [{ unitType: 'calls', consumed: 1 }],
219
+ triggerDeduplicationId: 'evt-9',
220
+ });
221
+ });
222
+ it('forwards a dev-returned failure result as-is (no body lift)', async () => {
223
+ const { client } = createFakePollingServiceClient();
224
+ const handlerFunction = jest
225
+ .fn()
226
+ .mockResolvedValue(OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.userInputError('dev-said-no')));
227
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
228
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
229
+ const result = await execution.execute({}, {
230
+ input: {},
231
+ event: { id: 'evt-1' },
232
+ });
233
+ expect(handlerFunction).toHaveBeenCalledTimes(1);
234
+ expect(result.isFailure).toBe(true);
235
+ const message = result._tag === 'Failure' ? result.error.message : '';
236
+ expect(message).toBe('dev-said-no');
237
+ });
238
+ it('does not treat a real event as a failure envelope unless BOTH error and message keys are present', async () => {
239
+ const { client } = createFakePollingServiceClient();
240
+ const handlerFunction = jest
241
+ .fn()
242
+ .mockResolvedValue(OperationHandler_1.OperationHandlerResult.success({ triggerDeduplicationId: 'evt-1' }));
243
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
244
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerPollDedup, 'new_records_request', client);
245
+ // Real event with only `error` (no `message`) -- not the failure envelope.
246
+ const result = await execution.execute({}, {
247
+ input: {},
248
+ event: { error: 'something' },
249
+ });
250
+ expect(handlerFunction).toHaveBeenCalledTimes(1);
251
+ expect(result.isSuccess).toBe(true);
252
+ });
253
+ });
254
+ describe('CompositeOperationExecution non-polling path (regression baseline)', () => {
255
+ it('invokes handlerFunction for non-polling handler types and returns its result', async () => {
256
+ const { client } = createFakePollingServiceClient();
257
+ const success = OperationHandler_1.OperationHandlerResult.success({ echoed: 42 });
258
+ const handlerFunction = jest.fn().mockResolvedValue(success);
259
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(handlerFunction);
260
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.TriggerRequest, 'some_request_op', client);
261
+ const ctx = {};
262
+ const input = { foo: 'bar' };
263
+ const result = await execution.execute(ctx, input);
264
+ expect(handlerFunction).toHaveBeenCalledTimes(1);
265
+ expect(handlerFunction).toHaveBeenCalledWith(ctx, input, expect.any(Function));
266
+ expect(result).toBe(success);
267
+ });
268
+ it('does not call the PollingServiceClient for non-polling handler types', async () => {
269
+ const { client, registerCalls, deregisterCalls } = createFakePollingServiceClient();
270
+ const handler = new CompositeOperationHandler_1.CompositeOperationHandler(jest.fn().mockResolvedValue(OperationHandler_1.OperationHandlerResult.success({})));
271
+ const execution = new CompositeOperationExecution_1.CompositeOperationExecution(noopHandlerInvocationFactory, handler, OperationHandler_1.OperationHandlerType.Regular, 'plain_regular_op', client);
272
+ await execution.execute({}, {});
273
+ expect(registerCalls).toHaveLength(0);
274
+ expect(deregisterCalls).toHaveLength(0);
275
+ });
276
+ });
@@ -2,9 +2,11 @@ import { OperationHandlerAuth, OperationHandlerType } from '@trayio/cdk-dsl/conn
2
2
  import { CompositeOperationHandler } from '@trayio/cdk-dsl/connector/operation/CompositeOperationHandler';
3
3
  import { OperationExecution } from './OperationExecution';
4
4
  import { OperationHandlerInvocationFactory } from './OperationHandlerInvocationFactory';
5
+ import { PollingServiceClient } from '../polling/PollingServiceClient';
5
6
  export declare class CompositeOperationExecutionFactory {
6
7
  private handlerInvocationFactory;
7
- constructor(handlerInvocationFactory: OperationHandlerInvocationFactory);
8
- executionFrom<AUTH extends OperationHandlerAuth<unknown, unknown>, IN, OUT>(handler: CompositeOperationHandler<AUTH, IN, OUT>, handlerType: OperationHandlerType): OperationExecution<AUTH, IN, OUT>;
8
+ private pollingServiceClient;
9
+ constructor(handlerInvocationFactory: OperationHandlerInvocationFactory, pollingServiceClient: PollingServiceClient);
10
+ executionFrom<AUTH extends OperationHandlerAuth<unknown, unknown>, IN, OUT>(handler: CompositeOperationHandler<AUTH, IN, OUT>, handlerType: OperationHandlerType, operationName: string): OperationExecution<AUTH, IN, OUT>;
9
11
  }
10
12
  //# sourceMappingURL=CompositeOperationExecutionFactory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CompositeOperationExecutionFactory.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecutionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAC;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAExF,qBAAa,kCAAkC;IAC9C,OAAO,CAAC,wBAAwB,CAAoC;gBAExD,wBAAwB,EAAE,iCAAiC;IAIvE,aAAa,CAAC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EACzE,OAAO,EAAE,yBAAyB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EACjD,WAAW,EAAE,oBAAoB,GAC/B,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;CAOpC"}
1
+ {"version":3,"file":"CompositeOperationExecutionFactory.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecutionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAC;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,qBAAa,kCAAkC;IAC9C,OAAO,CAAC,wBAAwB,CAAoC;IACpE,OAAO,CAAC,oBAAoB,CAAuB;gBAGlD,wBAAwB,EAAE,iCAAiC,EAC3D,oBAAoB,EAAE,oBAAoB;IAM3C,aAAa,CAAC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EACzE,OAAO,EAAE,yBAAyB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EACjD,WAAW,EAAE,oBAAoB,EACjC,aAAa,EAAE,MAAM,GACnB,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;CASpC"}
@@ -4,11 +4,13 @@ exports.CompositeOperationExecutionFactory = void 0;
4
4
  const CompositeOperationExecution_1 = require("./CompositeOperationExecution");
5
5
  class CompositeOperationExecutionFactory {
6
6
  handlerInvocationFactory;
7
- constructor(handlerInvocationFactory) {
7
+ pollingServiceClient;
8
+ constructor(handlerInvocationFactory, pollingServiceClient) {
8
9
  this.handlerInvocationFactory = handlerInvocationFactory;
10
+ this.pollingServiceClient = pollingServiceClient;
9
11
  }
10
- executionFrom(handler, handlerType) {
11
- return new CompositeOperationExecution_1.CompositeOperationExecution(this.handlerInvocationFactory, handler, handlerType);
12
+ executionFrom(handler, handlerType, operationName) {
13
+ return new CompositeOperationExecution_1.CompositeOperationExecution(this.handlerInvocationFactory, handler, handlerType, operationName, this.pollingServiceClient);
12
14
  }
13
15
  }
14
16
  exports.CompositeOperationExecutionFactory = CompositeOperationExecutionFactory;
@@ -0,0 +1,33 @@
1
+ import { OperationHandlerAuth, OperationHandlerContext, OperationHandlerResult, OperationHandlerType } from '@trayio/cdk-dsl/connector/operation/OperationHandler';
2
+ import { CompositeOperationHandler } from '@trayio/cdk-dsl/connector/operation/CompositeOperationHandler';
3
+ import { PollingTriggerInput } from '@trayio/cdk-dsl/connector/operation/PollingTypes';
4
+ import { PollingServiceClient } from '../polling/PollingServiceClient';
5
+ import { OperationHandlerInvocationFactory } from './OperationHandlerInvocationFactory';
6
+ export type PollingTriggerInvocationInput = PollingTriggerInput & {
7
+ initialCursor?: string | null;
8
+ };
9
+ export declare const isPollingLifecycle: (handlerType: OperationHandlerType) => boolean;
10
+ /**
11
+ * Polling dedup (`_request`) dispatch. Runs the Polling Service failure
12
+ * envelope short-circuit before the dev body, then lifts the dev's
13
+ * `triggerDeduplicationId` from the response body into
14
+ * `OperationHandlerResult.metadata` so ConnectorInvocationDomain's existing
15
+ * AppTalk metadata forwarding picks it up unchanged. The body is preserved
16
+ * as returned by the dev.
17
+ */
18
+ export declare function runTriggerPollDedup<AUTH extends OperationHandlerAuth<unknown, unknown>, IN, OUT>(args: {
19
+ ctx: OperationHandlerContext<AUTH>;
20
+ input: IN;
21
+ handler: CompositeOperationHandler<AUTH, IN, OUT>;
22
+ handlerInvocationFactory: OperationHandlerInvocationFactory;
23
+ validateOperationResponse: (r: OperationHandlerResult<OUT>) => void;
24
+ mapCaughtError: (e: unknown) => OperationHandlerResult<OUT>;
25
+ }): Promise<OperationHandlerResult<OUT>>;
26
+ export declare function runPollingRegisterOrDeregister<AUTH extends OperationHandlerAuth<unknown, unknown>>(args: {
27
+ ctx: OperationHandlerContext<AUTH>;
28
+ handlerType: OperationHandlerType.TriggerPollCreate | OperationHandlerType.TriggerPollDestroy;
29
+ operationName: string;
30
+ pollingInput: PollingTriggerInvocationInput;
31
+ pollingServiceClient: PollingServiceClient;
32
+ }): Promise<void>;
33
+ //# sourceMappingURL=CompositeOperationExecutionPolling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompositeOperationExecutionPolling.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/CompositeOperationExecutionPolling.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,uBAAuB,EAEvB,sBAAsB,EAEtB,oBAAoB,EAEpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAC;AAC1G,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAEvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAExF,MAAM,MAAM,6BAA6B,GAAG,mBAAmB,GAAG;IACjE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAuEF,eAAO,MAAM,kBAAkB,gBACjB,oBAAoB,KAC/B,OAEqD,CAAC;AAoCzD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EACnD,EAAE,EACF,GAAG,EACF,IAAI,EAAE;IACP,GAAG,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,yBAAyB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAClD,wBAAwB,EAAE,iCAAiC,CAAC;IAC5D,yBAAyB,EAAE,CAAC,CAAC,EAAE,sBAAsB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACpE,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,sBAAsB,CAAC,GAAG,CAAC,CAAC;CAC5D,GAAG,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAiCvC;AAED,wBAAgB,8BAA8B,CAC7C,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EAClD,IAAI,EAAE;IACP,GAAG,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACnC,WAAW,EACR,oBAAoB,CAAC,iBAAiB,GACtC,oBAAoB,CAAC,kBAAkB,CAAC;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,6BAA6B,CAAC;IAC5C,oBAAoB,EAAE,oBAAoB,CAAC;CAC3C,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDhB"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runPollingRegisterOrDeregister = exports.runTriggerPollDedup = exports.isPollingLifecycle = void 0;
4
+ const OperationHandler_1 = require("@trayio/cdk-dsl/connector/operation/OperationHandler");
5
+ const requirePollingCtxFields = (ctx) => {
6
+ const missing = [];
7
+ if (!ctx.connectorName)
8
+ missing.push('ctx.connectorName');
9
+ if (!ctx.connectorVersion)
10
+ missing.push('ctx.connectorVersion');
11
+ if (!ctx.executionToken)
12
+ missing.push('ctx.executionToken');
13
+ if (!ctx.publicUrl)
14
+ missing.push('ctx.publicUrl');
15
+ if (missing.length > 0) {
16
+ throw new Error(`polling lifecycle is missing required context fields: ${missing.join(', ')}`);
17
+ }
18
+ return {
19
+ connectorName: ctx.connectorName,
20
+ connectorVersion: ctx.connectorVersion,
21
+ executionToken: ctx.executionToken,
22
+ publicUrl: ctx.publicUrl,
23
+ };
24
+ };
25
+ const requirePollingRegisterCtxFields = (ctx) => {
26
+ const base = requirePollingCtxFields(ctx);
27
+ const missing = [];
28
+ if (!ctx.auth?.authId)
29
+ missing.push('ctx.auth.authId');
30
+ if (!ctx.organizationId)
31
+ missing.push('ctx.organizationId');
32
+ if (!ctx.workspaceId)
33
+ missing.push('ctx.workspaceId');
34
+ if (!ctx.projectId)
35
+ missing.push('ctx.projectId');
36
+ if (missing.length > 0) {
37
+ throw new Error(`polling register is missing required context fields: ${missing.join(', ')}`);
38
+ }
39
+ return {
40
+ ...base,
41
+ authenticationId: ctx.auth.authId,
42
+ organizationId: ctx.organizationId,
43
+ workspaceId: ctx.workspaceId,
44
+ projectId: ctx.projectId,
45
+ };
46
+ };
47
+ const isPollingLifecycle = (handlerType) => handlerType === OperationHandler_1.OperationHandlerType.TriggerPollCreate ||
48
+ handlerType === OperationHandler_1.OperationHandlerType.TriggerPollDestroy;
49
+ exports.isPollingLifecycle = isPollingLifecycle;
50
+ /**
51
+ * Polling Service failure envelope.
52
+ *
53
+ * After `maxFailureCount` consecutive non-recoverable failures from the dev's
54
+ * poll handler, the Polling Service posts a synthetic `{ error, message }`
55
+ * payload into the dedup (`_request`) operation instead of a real event. The
56
+ * connector cannot deduplicate that, so the runtime surfaces it as a
57
+ * platform userInputError before the dev's dedup body runs.
58
+ *
59
+ * Returns the short-circuit failure result when the event matches, or null
60
+ * when it is a real event that should flow to the dev body.
61
+ */
62
+ const detectPollingFailureEnvelope = (event) => {
63
+ if (event === null ||
64
+ event === undefined ||
65
+ typeof event !== 'object' ||
66
+ !('error' in event) ||
67
+ !('message' in event)) {
68
+ return null;
69
+ }
70
+ const payload = event;
71
+ return OperationHandler_1.OperationHandlerResult.failure(OperationHandler_1.OperationHandlerError.userInputError(typeof payload.message === 'string'
72
+ ? payload.message
73
+ : JSON.stringify(payload.message)));
74
+ };
75
+ /**
76
+ * Polling dedup (`_request`) dispatch. Runs the Polling Service failure
77
+ * envelope short-circuit before the dev body, then lifts the dev's
78
+ * `triggerDeduplicationId` from the response body into
79
+ * `OperationHandlerResult.metadata` so ConnectorInvocationDomain's existing
80
+ * AppTalk metadata forwarding picks it up unchanged. The body is preserved
81
+ * as returned by the dev.
82
+ */
83
+ async function runTriggerPollDedup(args) {
84
+ const { ctx, input, handler, handlerInvocationFactory, validateOperationResponse, mapCaughtError, } = args;
85
+ const event = input?.event;
86
+ const envelope = detectPollingFailureEnvelope(event);
87
+ if (envelope !== null) {
88
+ return envelope;
89
+ }
90
+ const handlerInvocation = handlerInvocationFactory(ctx);
91
+ try {
92
+ const result = await handler.handlerFunction(ctx, input, handlerInvocation);
93
+ validateOperationResponse(result);
94
+ if (!result.isSuccess) {
95
+ return result;
96
+ }
97
+ const body = result.value;
98
+ const mergedMetadata = {
99
+ ...(result.metadata ?? {}),
100
+ triggerDeduplicationId: body.triggerDeduplicationId,
101
+ };
102
+ return OperationHandler_1.OperationHandlerResult.success(result.value, mergedMetadata);
103
+ }
104
+ catch (error) {
105
+ return mapCaughtError(error);
106
+ }
107
+ }
108
+ exports.runTriggerPollDedup = runTriggerPollDedup;
109
+ function runPollingRegisterOrDeregister(args) {
110
+ const { ctx, handlerType, operationName: registeredHandlerName, pollingInput, pollingServiceClient, } = args;
111
+ switch (handlerType) {
112
+ case OperationHandler_1.OperationHandlerType.TriggerPollCreate: {
113
+ const registerCtx = requirePollingRegisterCtxFields(ctx);
114
+ return pollingServiceClient.register({
115
+ connectorName: registerCtx.connectorName,
116
+ connectorVersion: registerCtx.connectorVersion,
117
+ registeredHandlerName,
118
+ authenticationId: registerCtx.authenticationId,
119
+ organizationId: registerCtx.organizationId,
120
+ workspaceId: registerCtx.workspaceId,
121
+ projectId: registerCtx.projectId,
122
+ publicUrl: registerCtx.publicUrl,
123
+ pollingInterval: pollingInput.pollingInterval,
124
+ maxFailureCount: pollingInput.maxFailureCount,
125
+ initialCursor: pollingInput.initialCursor,
126
+ }, registerCtx.executionToken);
127
+ }
128
+ case OperationHandler_1.OperationHandlerType.TriggerPollDestroy: {
129
+ const destroyCtx = requirePollingCtxFields(ctx);
130
+ return pollingServiceClient.deregister({
131
+ connectorName: destroyCtx.connectorName,
132
+ connectorVersion: destroyCtx.connectorVersion,
133
+ registeredHandlerName,
134
+ publicUrl: destroyCtx.publicUrl,
135
+ preserveCursor: true,
136
+ }, destroyCtx.executionToken);
137
+ }
138
+ default:
139
+ throw new Error(`polling lifecycle called for non-polling handler type: ${String(handlerType)}`);
140
+ }
141
+ }
142
+ exports.runPollingRegisterOrDeregister = runPollingRegisterOrDeregister;
@@ -1 +1 @@
1
- {"version":3,"file":"OperationExecutionFactory.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/OperationExecutionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,gBAAgB,EAEhB,MAAM,2DAA2D,CAAC;AACnE,OAAO,EACN,oBAAoB,EAEpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAGxF,qBAAa,yBAAyB;IACrC,OAAO,CAAC,mCAAmC,CAAsC;IACjF,OAAO,CAAC,6BAA6B,CAAgC;IACrE,OAAO,CAAC,kCAAkC,CAAqC;gBAG9E,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,WAAW,EACxB,iCAAiC,EAAE,iCAAiC;IAYrE,aAAa,CAAC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EACzE,OAAO,EAAE,gBAAgB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,GACtC,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;IAWpC,OAAO,CAAC,2BAA2B;CAsBnC"}
1
+ {"version":3,"file":"OperationExecutionFactory.d.ts","sourceRoot":"","sources":["../../../src/connector/operation/OperationExecutionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,gBAAgB,EAEhB,MAAM,2DAA2D,CAAC;AACnE,OAAO,EACN,oBAAoB,EAEpB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAIxF,qBAAa,yBAAyB;IACrC,OAAO,CAAC,mCAAmC,CAAsC;IACjF,OAAO,CAAC,6BAA6B,CAAgC;IACrE,OAAO,CAAC,kCAAkC,CAAqC;gBAG9E,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,WAAW,EACxB,iCAAiC,EAAE,iCAAiC;IAerE,aAAa,CAAC,IAAI,SAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EACzE,OAAO,EAAE,gBAAgB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,GACtC,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC;IAWpC,OAAO,CAAC,2BAA2B;CAsBnC"}
@@ -4,6 +4,7 @@ exports.OperationExecutionFactory = void 0;
4
4
  const HttpOperationExecutionFactory_1 = require("./HttpOperationExecutionFactory");
5
5
  const CompositeOperationExecutionFactory_1 = require("./CompositeOperationExecutionFactory");
6
6
  const OperationExecutionValidationFactory_1 = require("./OperationExecutionValidationFactory");
7
+ const PollingServiceClient_1 = require("../polling/PollingServiceClient");
7
8
  class OperationExecutionFactory {
8
9
  operationExecutionValidationFactory;
9
10
  httpOperationExecutionFactory;
@@ -13,7 +14,7 @@ class OperationExecutionFactory {
13
14
  new OperationExecutionValidationFactory_1.OperationExecutionValidationFactory();
14
15
  this.httpOperationExecutionFactory = new HttpOperationExecutionFactory_1.HttpOperationExecutionFactory(httpClient, fileStorage);
15
16
  this.compositeOperationExecutionFactory =
16
- new CompositeOperationExecutionFactory_1.CompositeOperationExecutionFactory(operationHandlerInvocationFactory);
17
+ new CompositeOperationExecutionFactory_1.CompositeOperationExecutionFactory(operationHandlerInvocationFactory, new PollingServiceClient_1.PollingServiceClient(httpClient));
17
18
  }
18
19
  executionFrom(handler) {
19
20
  const executionFromImplementation = this.executionFromImplementation(handler);
@@ -25,7 +26,7 @@ class OperationExecutionFactory {
25
26
  case 'HttpOperationHandler':
26
27
  return this.httpOperationExecutionFactory.executionFrom(handler.implementation, handler.globalConfiguration, handler.handlerType);
27
28
  case 'CompositeOperationHandler':
28
- return this.compositeOperationExecutionFactory.executionFrom(handler.implementation, handler.handlerType);
29
+ return this.compositeOperationExecutionFactory.executionFrom(handler.implementation, handler.handlerType, handler.name);
29
30
  }
30
31
  }
31
32
  }
@@ -0,0 +1,50 @@
1
+ import { HttpClient } from '@trayio/commons/http/HttpClient';
2
+ import { PollingInterval } from '@trayio/cdk-dsl/connector/operation/PollingTypes';
3
+ export type PollingServiceRegisterPayload = {
4
+ connectorName: string;
5
+ connectorVersion: string;
6
+ /**
7
+ * The registered handler name (e.g. `"new_records_poll"`). The client
8
+ * normalizes to the canonical `_poll`-suffixed form before sending.
9
+ */
10
+ registeredHandlerName: string;
11
+ /** Platform identity that the Polling Service keys the registration against. */
12
+ authenticationId: string;
13
+ organizationId: string;
14
+ workspaceId: string;
15
+ projectId: string;
16
+ publicUrl: string;
17
+ pollingInterval?: PollingInterval;
18
+ maxFailureCount?: number;
19
+ initialCursor?: string | null;
20
+ };
21
+ export type PollingServiceDeregisterPayload = {
22
+ connectorName: string;
23
+ connectorVersion: string;
24
+ /**
25
+ * The registered handler name (e.g. `"new_records_poll_destroy"`). The
26
+ * client normalizes to the canonical `_poll`-suffixed form before sending.
27
+ */
28
+ registeredHandlerName: string;
29
+ publicUrl: string;
30
+ /**
31
+ * When true, the Polling Service retains the trigger's cursor so that a
32
+ * subsequent re-register (e.g. after a workflow is disabled then re-enabled)
33
+ * resumes from where polling stopped. The runtime always sends `true`
34
+ * today.
35
+ */
36
+ preserveCursor: boolean;
37
+ };
38
+ /**
39
+ * Internal HTTP client used by the runtime when invoking polling trigger
40
+ * create / destroy operations. Not exported from the public `@trayio/cdk-runtime`
41
+ * surface; connector developers must not call this directly.
42
+ */
43
+ export declare class PollingServiceClient {
44
+ private httpClient;
45
+ constructor(httpClient: HttpClient);
46
+ register(payload: PollingServiceRegisterPayload, executionToken: string): Promise<void>;
47
+ deregister(payload: PollingServiceDeregisterPayload, executionToken: string): Promise<void>;
48
+ private sendJson;
49
+ }
50
+ //# sourceMappingURL=PollingServiceClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PollingServiceClient.d.ts","sourceRoot":"","sources":["../../../src/connector/polling/PollingServiceClient.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAGN,eAAe,EAEf,MAAM,kDAAkD,CAAC;AA0B1D,MAAM,MAAM,6BAA6B,GAAG;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;AA4BF;;;;GAIG;AACH,qBAAa,oBAAoB;IACpB,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAEpC,QAAQ,CACb,OAAO,EAAE,6BAA6B,EACtC,cAAc,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAwBV,UAAU,CACf,OAAO,EAAE,+BAA+B,EACxC,cAAc,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;YAaF,QAAQ;CAgCtB"}
@@ -0,0 +1,128 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.PollingServiceClient = void 0;
27
+ const stream_1 = require("stream");
28
+ const E = __importStar(require("fp-ts/Either"));
29
+ const Http_1 = require("@trayio/commons/http/Http");
30
+ const PollingTypes_1 = require("@trayio/cdk-dsl/connector/operation/PollingTypes");
31
+ const POLLING_SERVICE_URL_ENV = 'TRAY_POLLING_SERVICE_URL';
32
+ const EXECUTION_TOKEN_HEADER = 'X-Tray-Execution-Token';
33
+ const TRIGGERS_REGISTER_PATH = '/triggers/register';
34
+ const POLL_SUFFIX = '_poll';
35
+ const POLL_DESTROY_SUFFIX = '_poll_destroy';
36
+ /**
37
+ * PS keys register+deregister against the canonical `_poll`-suffixed
38
+ * operation name. `_poll_destroy` handlers rewrite to the matching `_poll`
39
+ * name so both sides of the lifecycle target the same PS record.
40
+ */
41
+ const toCanonicalPollOperationName = (registeredHandlerName) => {
42
+ if (registeredHandlerName.endsWith(POLL_DESTROY_SUFFIX)) {
43
+ return `${registeredHandlerName.slice(0, -POLL_DESTROY_SUFFIX.length)}${POLL_SUFFIX}`;
44
+ }
45
+ return registeredHandlerName;
46
+ };
47
+ const pollingIntervalToMinutes = (interval) => {
48
+ switch (interval.unit) {
49
+ case PollingTypes_1.PollingIntervalUnit.Minutes:
50
+ return interval.value;
51
+ case PollingTypes_1.PollingIntervalUnit.Hours:
52
+ return interval.value * 60;
53
+ default:
54
+ throw new Error(`Unsupported pollingInterval.unit: ${interval.unit}`);
55
+ }
56
+ };
57
+ const resolveEndpoint = () => {
58
+ const baseUrl = process.env[POLLING_SERVICE_URL_ENV];
59
+ if (!baseUrl) {
60
+ throw new Error(`${POLLING_SERVICE_URL_ENV} env var is not set; the polling service cannot be reached`);
61
+ }
62
+ return `${baseUrl.replace(/\/$/, '')}${TRIGGERS_REGISTER_PATH}`;
63
+ };
64
+ const bodyToReadable = (payload) => stream_1.Readable.from(JSON.stringify(payload));
65
+ /**
66
+ * Internal HTTP client used by the runtime when invoking polling trigger
67
+ * create / destroy operations. Not exported from the public `@trayio/cdk-runtime`
68
+ * surface; connector developers must not call this directly.
69
+ */
70
+ class PollingServiceClient {
71
+ httpClient;
72
+ constructor(httpClient) {
73
+ this.httpClient = httpClient;
74
+ }
75
+ async register(payload, executionToken) {
76
+ const intervalMinutes = pollingIntervalToMinutes(payload.pollingInterval ?? PollingTypes_1.DEFAULT_POLLING_INTERVAL);
77
+ const maxFailureCount = payload.maxFailureCount ?? PollingTypes_1.DEFAULT_MAX_FAILURE_COUNT;
78
+ const body = {
79
+ connectorName: payload.connectorName,
80
+ connectorVersion: payload.connectorVersion,
81
+ operationName: toCanonicalPollOperationName(payload.registeredHandlerName),
82
+ authenticationId: payload.authenticationId,
83
+ organizationId: payload.organizationId,
84
+ workspaceId: payload.workspaceId,
85
+ projectId: payload.projectId,
86
+ publicUrl: payload.publicUrl,
87
+ interval: intervalMinutes,
88
+ maxFailureCount,
89
+ initialCursor: payload.initialCursor ?? null,
90
+ };
91
+ await this.sendJson(Http_1.HttpMethod.Post, executionToken, body);
92
+ }
93
+ async deregister(payload, executionToken) {
94
+ const body = {
95
+ connectorName: payload.connectorName,
96
+ connectorVersion: payload.connectorVersion,
97
+ operationName: toCanonicalPollOperationName(payload.registeredHandlerName),
98
+ publicUrl: payload.publicUrl,
99
+ preserveCursor: payload.preserveCursor,
100
+ };
101
+ await this.sendJson(Http_1.HttpMethod.Delete, executionToken, body);
102
+ }
103
+ async sendJson(method, executionToken, body) {
104
+ if (!executionToken) {
105
+ throw new Error('polling service call requires an execution token (ctx.executionToken)');
106
+ }
107
+ const url = resolveEndpoint();
108
+ const request = Http_1.HttpRequest.create({
109
+ headers: {
110
+ [Http_1.HttpHeader.ContentType]: 'application/json',
111
+ [EXECUTION_TOKEN_HEADER]: executionToken,
112
+ },
113
+ body: bodyToReadable(body),
114
+ });
115
+ const responseE = await this.httpClient.execute(method, url, request)();
116
+ if (E.isLeft(responseE)) {
117
+ throw responseE.left;
118
+ }
119
+ const response = responseE.right;
120
+ if (response.statusCode < 200 || response.statusCode >= 300) {
121
+ throw new Error(`polling service ${method} ${url} failed with status ${response.statusCode}`);
122
+ }
123
+ // Drain the body so the underlying HTTP connection can be released.
124
+ // We don't need the payload; the status code is authoritative.
125
+ response.body.resume();
126
+ }
127
+ }
128
+ exports.PollingServiceClient = PollingServiceClient;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=PollingServiceClient.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PollingServiceClient.unit.test.d.ts","sourceRoot":"","sources":["../../../src/connector/polling/PollingServiceClient.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,218 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const stream_1 = require("stream");
27
+ const TE = __importStar(require("fp-ts/TaskEither"));
28
+ const Http_1 = require("@trayio/commons/http/Http");
29
+ const PollingTypes_1 = require("@trayio/cdk-dsl/connector/operation/PollingTypes");
30
+ const PollingServiceClient_1 = require("./PollingServiceClient");
31
+ const BASE_URL = 'https://polling.example.test';
32
+ const ENDPOINT_PATH = '/triggers/register';
33
+ const ENV_VAR = 'TRAY_POLLING_SERVICE_URL';
34
+ const createMockHttpClient = (capture) => ({
35
+ execute: jest.fn().mockImplementation((method, url, request) => TE.tryCatch(async () => {
36
+ const chunks = [];
37
+ for await (const chunk of request.body) {
38
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
39
+ }
40
+ const raw = Buffer.concat(chunks).toString('utf8');
41
+ capture.push({
42
+ method,
43
+ url,
44
+ headers: request.headers,
45
+ body: raw.length > 0 ? JSON.parse(raw) : undefined,
46
+ });
47
+ return {
48
+ statusCode: 200,
49
+ headers: {},
50
+ body: stream_1.Readable.from(''),
51
+ };
52
+ }, (reason) => new Error(String(reason)))),
53
+ });
54
+ const minimalRegister = (overrides = {}) => ({
55
+ connectorName: 'x',
56
+ connectorVersion: '1.0.0',
57
+ registeredHandlerName: 'x_poll',
58
+ authenticationId: 'auth-uuid',
59
+ organizationId: 'org-id',
60
+ workspaceId: 'ws-id',
61
+ projectId: 'project-uuid',
62
+ publicUrl: 'u',
63
+ ...overrides,
64
+ });
65
+ describe('PollingServiceClient', () => {
66
+ let originalEnv;
67
+ beforeEach(() => {
68
+ originalEnv = process.env[ENV_VAR];
69
+ process.env[ENV_VAR] = BASE_URL;
70
+ });
71
+ afterEach(() => {
72
+ if (originalEnv === undefined) {
73
+ delete process.env[ENV_VAR];
74
+ }
75
+ else {
76
+ process.env[ENV_VAR] = originalEnv;
77
+ }
78
+ });
79
+ describe('register', () => {
80
+ it('POSTs to /triggers/register with the full payload and execution token header', async () => {
81
+ const captured = [];
82
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
83
+ await client.register({
84
+ connectorName: 'salesforce',
85
+ connectorVersion: '7.2.0',
86
+ registeredHandlerName: 'new_records_poll',
87
+ authenticationId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
88
+ organizationId: 'org-uuid',
89
+ workspaceId: 'ws-uuid',
90
+ projectId: '7fa00000-1111-2222-3333-444455556666',
91
+ publicUrl: 'https://trigger.example.test/abc',
92
+ pollingInterval: {
93
+ value: 10,
94
+ unit: PollingTypes_1.PollingIntervalUnit.Minutes,
95
+ },
96
+ maxFailureCount: 42,
97
+ initialCursor: 'cursor-0',
98
+ }, 'exec-jwt-token');
99
+ expect(captured).toHaveLength(1);
100
+ const call = captured[0];
101
+ expect(call.method).toBe(Http_1.HttpMethod.Post);
102
+ expect(call.url).toBe(`${BASE_URL}${ENDPOINT_PATH}`);
103
+ expect(call.headers).toMatchObject({
104
+ 'Content-Type': 'application/json',
105
+ 'X-Tray-Execution-Token': 'exec-jwt-token',
106
+ });
107
+ expect(call.body).toEqual({
108
+ connectorName: 'salesforce',
109
+ connectorVersion: '7.2.0',
110
+ operationName: 'new_records_poll',
111
+ authenticationId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
112
+ organizationId: 'org-uuid',
113
+ workspaceId: 'ws-uuid',
114
+ projectId: '7fa00000-1111-2222-3333-444455556666',
115
+ publicUrl: 'https://trigger.example.test/abc',
116
+ interval: 10,
117
+ maxFailureCount: 42,
118
+ initialCursor: 'cursor-0',
119
+ });
120
+ });
121
+ it('sends interval in minutes for PollingIntervalUnit.Minutes', async () => {
122
+ const captured = [];
123
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
124
+ await client.register(minimalRegister({
125
+ pollingInterval: {
126
+ value: 15,
127
+ unit: PollingTypes_1.PollingIntervalUnit.Minutes,
128
+ },
129
+ }), 'tok');
130
+ expect(captured[0].body.interval).toBe(15);
131
+ });
132
+ it('converts hours to minutes', async () => {
133
+ const captured = [];
134
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
135
+ await client.register(minimalRegister({
136
+ pollingInterval: { value: 2, unit: PollingTypes_1.PollingIntervalUnit.Hours },
137
+ }), 'tok');
138
+ expect(captured[0].body.interval).toBe(120);
139
+ });
140
+ it('falls back to defaults (5 min, maxFailureCount 30, null cursor) when omitted', async () => {
141
+ const captured = [];
142
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
143
+ await client.register(minimalRegister(), 'tok');
144
+ expect(captured[0].body).toMatchObject({
145
+ interval: 5,
146
+ maxFailureCount: 30,
147
+ initialCursor: null,
148
+ });
149
+ });
150
+ it('trims trailing slashes from the PS base URL', async () => {
151
+ process.env[ENV_VAR] = `${BASE_URL}/`;
152
+ const captured = [];
153
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
154
+ await client.register(minimalRegister(), 'tok');
155
+ expect(captured[0].url).toBe(`${BASE_URL}${ENDPOINT_PATH}`);
156
+ });
157
+ it('throws when TRAY_POLLING_SERVICE_URL env var is unset', async () => {
158
+ delete process.env[ENV_VAR];
159
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient([]));
160
+ await expect(client.register(minimalRegister(), 'tok')).rejects.toThrow(/TRAY_POLLING_SERVICE_URL/);
161
+ });
162
+ it('throws when execution token is an empty string', async () => {
163
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient([]));
164
+ await expect(client.register(minimalRegister(), '')).rejects.toThrow(/execution token/);
165
+ });
166
+ it('throws when PS returns a non-2xx status', async () => {
167
+ const errorClient = {
168
+ execute: () => TE.right({
169
+ statusCode: 500,
170
+ headers: {},
171
+ body: stream_1.Readable.from(''),
172
+ }),
173
+ };
174
+ const client = new PollingServiceClient_1.PollingServiceClient(errorClient);
175
+ await expect(client.register(minimalRegister(), 'tok')).rejects.toThrow(/failed with status 500/);
176
+ });
177
+ });
178
+ describe('deregister', () => {
179
+ it('DELETEs /triggers/register with canonical op name, preserveCursor flag and execution token header', async () => {
180
+ const captured = [];
181
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
182
+ await client.deregister({
183
+ connectorName: 'salesforce',
184
+ connectorVersion: '7.2.0',
185
+ registeredHandlerName: 'new_records_poll_destroy',
186
+ publicUrl: 'https://trigger.example.test/abc',
187
+ preserveCursor: true,
188
+ }, 'exec-jwt-token');
189
+ expect(captured).toHaveLength(1);
190
+ const call = captured[0];
191
+ expect(call.method).toBe(Http_1.HttpMethod.Delete);
192
+ expect(call.url).toBe(`${BASE_URL}${ENDPOINT_PATH}`);
193
+ expect(call.headers).toMatchObject({
194
+ 'X-Tray-Execution-Token': 'exec-jwt-token',
195
+ });
196
+ expect(call.body).toEqual({
197
+ connectorName: 'salesforce',
198
+ connectorVersion: '7.2.0',
199
+ operationName: 'new_records_poll',
200
+ publicUrl: 'https://trigger.example.test/abc',
201
+ preserveCursor: true,
202
+ });
203
+ });
204
+ it('leaves an already-canonical _poll name unchanged', async () => {
205
+ const captured = [];
206
+ const client = new PollingServiceClient_1.PollingServiceClient(createMockHttpClient(captured));
207
+ await client.deregister({
208
+ connectorName: 'x',
209
+ connectorVersion: '1.0.0',
210
+ registeredHandlerName: 'x_poll',
211
+ publicUrl: 'u',
212
+ preserveCursor: false,
213
+ }, 'tok');
214
+ expect(captured[0].body.operationName).toBe('x_poll');
215
+ expect(captured[0].body.preserveCursor).toBe(false);
216
+ });
217
+ });
218
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trayio/cdk-runtime",
3
- "version": "5.21.0",
3
+ "version": "5.22.0-unstable",
4
4
  "description": "A Runtime that executes connector operations defined using the CDK DSL",
5
5
  "exports": {
6
6
  "./*": "./dist/*.js"
@@ -14,10 +14,10 @@
14
14
  "node": ">=18.x"
15
15
  },
16
16
  "dependencies": {
17
- "@trayio/axios": "5.21.0",
18
- "@trayio/cdk-dsl": "5.21.0",
19
- "@trayio/express": "5.21.0",
20
- "@trayio/winston": "5.21.0",
17
+ "@trayio/axios": "5.22.0-unstable",
18
+ "@trayio/cdk-dsl": "5.22.0-unstable",
19
+ "@trayio/express": "5.22.0-unstable",
20
+ "@trayio/winston": "5.22.0-unstable",
21
21
  "mime": "3.0.0",
22
22
  "uuid": "9.0.0"
23
23
  },
@@ -34,5 +34,6 @@
34
34
  ],
35
35
  "devDependencies": {
36
36
  "just-permutations": "^2.2.1"
37
- }
37
+ },
38
+ "stableVersion": "0.0.0"
38
39
  }