@skedulo/integration-sdk 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## [0.5.3]
3
+ ## [0.5.5]
4
4
 
5
5
  **Direction-agnostic relationship lookup resolution.**
6
6
 
7
- - `IdMappingService.resolveLookups` — relationship lookups now resolve regardless of which direction the related object was synced. Forward-first (own-direction, by `SourceId`, unchanged), then a reverse fallback that matches by `TargetId` under the swapped system pair and returns the row's `SourceId`. Fixes `id_mapping_lookup_miss` when a relationship synced one way is used by a flow going the other (e.g. companies synced HubSpot→Skedulo, resolved by an outbound Skedulo→HubSpot flow).
8
- - The reverse pass relaxes `ObjectType` (the lookup rule only knows the near-side object name) and guards against wrong matches: an id matching more than one distinct counterpart is left unresolved (`id_mapping_reverse_ambiguous`), and a unique-looking match on a limit-filling page is distrusted in case a sibling row was truncated (`id_mapping_reverse_truncated`) rather than guessed.
9
- - Primary identity still resolves own-direction only (it decides create vs. update). `IdMappingSyncStrategy` routes primary IDs through `resolve()` and relationship fields through `resolveLookups()` with separate maps; applies to both batch and event paths. The event path wraps resolution so a failure becomes a controlled `failed` result instead of an uncaught throw, matching the batch path.
7
+ - `IdMappingService.resolveLookups` — relationship lookups now resolve regardless of which direction the related object was synced. One query per object matches the id on either side of the system pair: `(src→tgt, ObjectType scoped, SourceId IN ids)` OR `(tgt→src, TargetId IN ids)`; the counterpart is the other system's id. Fixes `id_mapping_lookup_miss` when a relationship synced one way is used by a flow going the other (e.g. companies synced HubSpot→Skedulo, resolved by an outbound Skedulo→HubSpot flow).
8
+ - Own-direction (forward) matches take precedence — a single forward match wins and reverse rows are ignored; reverse only decides when forward is empty. Ambiguity within the winning tier (>1 distinct counterpart) is left unresolved (`id_mapping_lookup_ambiguous`). The query is paginated, so ambiguity detection always sees every matching row.
9
+ - Both the batch and event paths build their lookup queries through the same helper and run the same resolution, so they can't diverge. Primary identity still resolves own-direction only via `resolve()` (it decides create vs. update). The event path wraps resolution so a failure becomes a controlled `failed` result instead of an uncaught throw, matching the batch path.
10
+
11
+ **Fixes**
12
+
13
+ - Concurrent deliveries of the same new record (e.g. a Skedulo `INSERT` plus a near-instant `UPDATE`) no longer create duplicate target records. Before writing a create, `IdMappingSyncStrategy` reserves the record's `UniqueKey` via `IdMappingService.claimCreates` (an `insert` the unique constraint rejects for a racing duplicate); the winner creates and finalizes the reservation with the real `TargetId`, a loser skips (its change reconciles on the next sync) rather than inserting a second record. A failed target write releases the reservation so a stale row can't permanently block re-creation. Covers both `processBatch` (batch + `event.on-skedulo`) and the single-event path.
14
+ - `DataFlowService.deactivate` no longer fails with `409 "Deactivate the flow to edit"`. It now flips IS to INACTIVE before clearing `trigger_name` (IS rejects edits to an active flow), preserving the clear-before-delete ordering that keeps a mid-failure recoverable.
15
+ - `event.on-skedulo` now processes a multi-record delivery as a **batch** (`SyncRunner.handleFlowEventBatch`) — upserts run through the shared `SyncService.processBatch` path (one grouped `resolveLookups` + one insert/update batch for all records) instead of resolving and writing each record one at a time; deletes stay per-record. Resolves the real source adapter so the source id is read correctly (Skedulo → `UID`), matching the batch sync path.
16
+ - `buildArtifactName` now names each object with its system — `sked-int-<sourceSystem>-<sourceObject>-to-<targetSystem>-<targetObject>-flow<id>` — so an outbound flow reads `sked-int-skedulo-jobs-to-salesforce-…` instead of `sked-int-salesforce-jobs-to-…`, where the vendor prefix looked like part of the (actually Skedulo) source object. Falls back to `skedulo`/`<integrationType>` for legacy flows with no explicit systems. **Existing realtime flows get a renamed triggered action on their next deactivate→reactivate cycle** (deactivate deletes the old artifact by its stored name, so cleanup is unaffected).
10
17
 
11
18
  ## [0.5.2]
12
19
 
@@ -1 +1 @@
1
- {"version":3,"file":"on-skedulo.action.d.ts","sourceRoot":"","sources":["../../../src/actions/event/on-skedulo.action.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,EAAiB,MAAM,SAAS,CAAC;AAkCnG,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,GACd,MAAM,CAAC,mBAAmB,EAAE,oBAAoB,CAAC,CA8CnD"}
1
+ {"version":3,"file":"on-skedulo.action.d.ts","sourceRoot":"","sources":["../../../src/actions/event/on-skedulo.action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,EAAiB,MAAM,SAAS,CAAC;AAkCnG,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,GACd,MAAM,CAAC,mBAAmB,EAAE,oBAAoB,CAAC,CA+CnD"}
@@ -9,7 +9,7 @@ const errors_1 = require("../utils/errors");
9
9
  * Parse one element of the ElasticServer triggered-action `call_url` body. Each
10
10
  * element is the GraphQL query result wrapped under a single subscription-name
11
11
  * key inside `data`, plus an `operation`, e.g.
12
- * `{ "data": { "contacts": { "UID": "…", "Email": … } }, "operation": "INSERT" }`
12
+ * `[{ "data": { "contacts": { "UID": "…", "Email": … } }, "operation": "INSERT" }]`
13
13
  */
14
14
  function parseSkeduloChange(raw, index) {
15
15
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
@@ -39,42 +39,40 @@ function makeOnSkeduloEventAction(deps) {
39
39
  return {
40
40
  name: action_names_1.ACTIONS.event.onSkedulo,
41
41
  parseInput(raw, meta) {
42
- var _a;
43
- const dataFlowId = (_a = meta === null || meta === void 0 ? void 0 : meta.dataFlowId) !== null && _a !== void 0 ? _a : raw === null || raw === void 0 ? void 0 : raw.dataFlowId;
42
+ // The flow id always rides on the `sked-data-flow-id` header (lifted into meta).
43
+ const dataFlowId = meta === null || meta === void 0 ? void 0 : meta.dataFlowId;
44
44
  if (!Number.isInteger(dataFlowId) || dataFlowId <= 0) {
45
- throw new errors_1.InvalidInputError('dataFlowId must be a positive integer');
45
+ throw new errors_1.InvalidInputError('dataFlowId must be a positive integer (from the sked-data-flow-id header)');
46
46
  }
47
- // ES posts an array; tolerate `{ changes: [...] }` or a single object for
48
- // manual / testing calls.
49
- const obj = (raw !== null && raw !== void 0 ? raw : {});
50
- const elements = Array.isArray(raw) ? raw : Array.isArray(obj.changes) ? obj.changes : [raw];
51
- const changes = elements.map(parseSkeduloChange);
47
+ // The ElasticServer triggered action always posts a bare array of change elements.
48
+ if (!Array.isArray(raw)) {
49
+ throw new errors_1.InvalidInputError('expected an array of Skedulo change events');
50
+ }
51
+ const changes = raw.map(parseSkeduloChange);
52
52
  return { dataFlowId: dataFlowId, changes };
53
53
  },
54
54
  async handler(input, ctx) {
55
55
  const integrationType = deps.adapter.integrationType;
56
56
  const lookupStrategy = (0, helpers_1.resolveLookupStrategy)(deps.adapter);
57
57
  const runner = new sync_runner_1.SyncRunner(deps.registry);
58
- const results = [];
59
- for (const change of input.changes) {
60
- const result = await runner.handleFlowEvent({
61
- flowId: input.dataFlowId,
62
- event: {
63
- integrationType,
64
- objectType: '',
65
- operation: change.operation,
66
- recordId: change.recordId,
67
- payload: change.payload,
68
- dataFlowId: input.dataFlowId,
69
- },
70
- options: { lookupStrategy },
71
- }, ctx);
72
- results.push(result);
73
- }
74
- if (results.some((r) => r.status === 'failed')) {
75
- throw new errors_1.UpstreamPermanentError('Skedulo event processing failed', { details: results });
58
+ // One delivery can carry many record changes — process them as a batch so
59
+ // resolution and writes happen once for the whole set, not per record.
60
+ const result = await runner.handleFlowEventBatch({
61
+ flowId: input.dataFlowId,
62
+ events: input.changes.map((change) => ({
63
+ integrationType,
64
+ objectType: '',
65
+ operation: change.operation,
66
+ recordId: change.recordId,
67
+ payload: change.payload,
68
+ dataFlowId: input.dataFlowId,
69
+ })),
70
+ options: { lookupStrategy },
71
+ }, ctx);
72
+ if (result.status === 'failed') {
73
+ throw new errors_1.UpstreamPermanentError('Skedulo event processing failed', { details: result });
76
74
  }
77
- return { message: 'Skedulo event processed', results };
75
+ return { message: 'Skedulo event processed', results: [result] };
78
76
  },
79
77
  };
80
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"on-skedulo.action.js","sourceRoot":"","sources":["../../../src/actions/event/on-skedulo.action.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"on-skedulo.action.js","sourceRoot":"","sources":["../../../src/actions/event/on-skedulo.action.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ export declare const OperationName: {
13
13
  readonly PUSH_FETCH: "pushFetch";
14
14
  readonly PREVIEW: (id: number) => string;
15
15
  readonly MAPPING_RESOLVE: "idMappingResolve";
16
+ readonly MAPPING_CLAIM: "idMappingClaim";
16
17
  readonly MAPPING_UPSERT: "idMappingUpsert";
17
18
  readonly MAPPING_DELETE_LOOKUP: "idMappingDeleteLookup";
18
19
  readonly MAPPING_DELETE: "idMappingDelete";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,eAAO,MAAM,aAAa;;gCAEL,MAAM;;;wBAGd,MAAM;0CACY,MAAM;;2BAErB,MAAM;;;;;;CAMZ,CAAC;AAEX,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,oFAAoF;AACpF,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAElF,wEAAwE;AACxE,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,eAAO,MAAM,WAAW;;;CAGd,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,cAAc;IACzB,kFAAkF;;IAElF,sFAAsF;;CAE9E,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,eAAO,MAAM,aAAa;;gCAEL,MAAM;;;wBAGd,MAAM;0CACY,MAAM;;2BAErB,MAAM;;;;;;;CAOZ,CAAC;AAEX,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,oFAAoF;AACpF,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAElF,wEAAwE;AACxE,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,eAAO,MAAM,WAAW;;;CAGd,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,cAAc;IACzB,kFAAkF;;IAElF,sFAAsF;;CAE9E,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC"}
package/dist/constants.js CHANGED
@@ -16,6 +16,7 @@ exports.OperationName = {
16
16
  PUSH_FETCH: 'pushFetch',
17
17
  PREVIEW: (id) => `preview_${id}`,
18
18
  MAPPING_RESOLVE: 'idMappingResolve',
19
+ MAPPING_CLAIM: 'idMappingClaim',
19
20
  MAPPING_UPSERT: 'idMappingUpsert',
20
21
  MAPPING_DELETE_LOOKUP: 'idMappingDeleteLookup',
21
22
  MAPPING_DELETE: 'idMappingDelete',
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,QAAQ,GAAG;IACtB,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;CAChC,CAAC;AAEE,QAAA,aAAa,GAAG;IAC3B,eAAe,EAAE,gBAAgB;IACjC,YAAY,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,EAAE,EAAE;IAClD,qBAAqB,EAAE,6BAA6B;IACpD,cAAc,EAAE,uBAAuB;IACvC,IAAI,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE;IAClC,cAAc,EAAE,CAAC,UAAkB,EAAE,EAAE,CAAC,iBAAiB,UAAU,EAAE;IACrE,UAAU,EAAE,WAAW;IACvB,OAAO,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,WAAW,EAAE,EAAE;IACxC,eAAe,EAAE,kBAAkB;IACnC,cAAc,EAAE,iBAAiB;IACjC,qBAAqB,EAAE,uBAAuB;IAC9C,cAAc,EAAE,iBAAiB;IACjC,mBAAmB,EAAE,qBAAqB;CAClC,CAAC;AAEE,QAAA,iBAAiB,GAAG,GAAG,CAAC;AAErC,oFAAoF;AACvE,QAAA,cAAc,GAAG;IAC5B,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAGX,wEAAwE;AAC3D,QAAA,cAAc,GAAG,SAAS,CAAC;AAE3B,QAAA,WAAW,GAAG;IACzB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACR,CAAC;AAGE,QAAA,QAAQ,GAAG;IACtB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;CACN,CAAC;AAGE,QAAA,cAAc,GAAG;IAC5B,kFAAkF;IAClF,UAAU,EAAE,YAAY;IACxB,sFAAsF;IACtF,SAAS,EAAE,WAAW;CACd,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,QAAQ,GAAG;IACtB,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;CAChC,CAAC;AAEE,QAAA,aAAa,GAAG;IAC3B,eAAe,EAAE,gBAAgB;IACjC,YAAY,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,EAAE,EAAE;IAClD,qBAAqB,EAAE,6BAA6B;IACpD,cAAc,EAAE,uBAAuB;IACvC,IAAI,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE;IAClC,cAAc,EAAE,CAAC,UAAkB,EAAE,EAAE,CAAC,iBAAiB,UAAU,EAAE;IACrE,UAAU,EAAE,WAAW;IACvB,OAAO,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,WAAW,EAAE,EAAE;IACxC,eAAe,EAAE,kBAAkB;IACnC,aAAa,EAAE,gBAAgB;IAC/B,cAAc,EAAE,iBAAiB;IACjC,qBAAqB,EAAE,uBAAuB;IAC9C,cAAc,EAAE,iBAAiB;IACjC,mBAAmB,EAAE,qBAAqB;CAClC,CAAC;AAEE,QAAA,iBAAiB,GAAG,GAAG,CAAC;AAErC,oFAAoF;AACvE,QAAA,cAAc,GAAG;IAC5B,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAGX,wEAAwE;AAC3D,QAAA,cAAc,GAAG,SAAS,CAAC;AAE3B,QAAA,WAAW,GAAG;IACzB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACR,CAAC;AAGE,QAAA,QAAQ,GAAG;IACtB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;CACN,CAAC;AAGE,QAAA,cAAc,GAAG;IAC5B,kFAAkF;IAClF,UAAU,EAAE,YAAY;IACxB,sFAAsF;IACtF,SAAS,EAAE,WAAW;CACd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dataflow.service.d.ts","sourceRoot":"","sources":["../../src/services/dataflow.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,OAAO,EAGL,KAAK,QAAQ,EAEd,MAAM,wCAAwC,CAAC;AAUhD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAE7C,gBAAgB,CAAC,IAAI,EAAE;QAC3B,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAajB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIjD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,UAAU,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAajB,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAIhE,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIjE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAyDtE;;;;OAIG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;CAmCzE"}
1
+ {"version":3,"file":"dataflow.service.d.ts","sourceRoot":"","sources":["../../src/services/dataflow.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,OAAO,EAGL,KAAK,QAAQ,EAEd,MAAM,wCAAwC,CAAC;AAUhD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAE7C,gBAAgB,CAAC,IAAI,EAAE;QAC3B,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAajB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIjD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,UAAU,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAajB,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAIhE,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIjE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAyDtE;;;;OAIG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;CAsCzE"}
@@ -140,13 +140,17 @@ class DataFlowService {
140
140
  throw new errors_1.NotFoundError('FLOW_NOT_FOUND', `Flow ${id} not found`);
141
141
  }
142
142
  const sc = (0, data_flow_spec_interface_1.asSyncConfig)((_a = flow.triggerConfig) !== null && _a !== void 0 ? _a : null);
143
- // Clear trigger_name in IS FIRST. If we deleted the artifact before clearing
143
+ // Flip IS to INACTIVE FIRST. IS rejects any edit to an active flow
144
+ // (409 "Deactivate the flow to edit"), so the trigger_name clear below — a
145
+ // PATCH — is only permitted once the flow is inactive.
146
+ await this.client.deactivate(id);
147
+ // Then clear trigger_name BEFORE deleting the artifact. If we deleted first
144
148
  // and the clear failed, the flow would be left INACTIVE with a stale
145
149
  // trigger_name — the next activate would see it, take the "already
146
150
  // provisioned" branch, and mark ACTIVE without recreating the artifact
147
- // (silent break). Clearing first means any later failure is recoverable:
151
+ // (silent break). Clearing first keeps every later failure recoverable:
148
152
  // re-running deactivate is a no-op on the already-cleared config and
149
- // safely (re-)attempts the artifact delete + IS status flip.
153
+ // safely (re-)attempts the artifact delete.
150
154
  if (sc) {
151
155
  const { trigger_name: _n, trigger_type: _t } = sc, rest = __rest(sc, ["trigger_name", "trigger_type"]);
152
156
  void _n;
@@ -160,7 +164,6 @@ class DataFlowService {
160
164
  const client = ctx.newArtifactClient(pulse_solution_services_1.ArtifactType.TRIGGERED_ACTION);
161
165
  await (0, artifact_builder_1.deleteArtifactIfExists)(client, sc.trigger_name);
162
166
  }
163
- await this.client.deactivate(id);
164
167
  const updated = await this.client.getById(id);
165
168
  return updated !== null && updated !== void 0 ? updated : flow;
166
169
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dataflow.service.js","sourceRoot":"","sources":["../../src/services/dataflow.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataflow.service.js","sourceRoot":"","sources":["../../src/services/dataflow.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,8EAAgE;AAQhE,oDAAwD;AACxD,4CAA8C;AAC9C,qFAKgD;AAChD,gEAOmC;AAEnC,MAAa,eAAe;IAC1B,YAA6B,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;IAAG,CAAC;IAEvD,KAAK,CAAC,gBAAgB,CAAC,IAItB;QACC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAClC,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,0BAAc,CAAC,MAAM;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,MAAmB;QACpD,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAGhB;QACC,MAAM,MAAM,GAAuB;YACjC,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,MAAM,EAAE,0BAAc,CAAC,MAAM;SAC9B,CAAC;QACF,IAAI,MAAM,CAAC,YAAY;YAAE,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QAEnE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAA0B;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAA0B;QACrC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,KAA0B;QACjD,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAU,EAAE,GAAuB;;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,sBAAa,CAAC,gBAAgB,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,EAAE,GAAG,IAAA,uCAAY,EAAC,MAAA,IAAI,CAAC,aAAa,mCAAI,IAAI,CAAC,CAAC;QAEpD,0EAA0E;QAC1E,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC9C,OAAO,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,IAAI,CAAC;QACzB,CAAC;QAED,IAAI,EAAE,CAAC,YAAY,EAAE,CAAC;YACpB,iDAAiD;YACjD,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC9C,OAAO,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,IAAI,CAAC;QACzB,CAAC;QAED,MAAM,IAAI,GAAG,IAAA,oCAAiB,EAAC,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAA,sCAAmB,EAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAA,+CAA4B,EAAC;YAC5C,IAAI;YACJ,GAAG;YACH,UAAU,EAAE,IAAI,CAAC,YAAY;YAC7B,KAAK,EAAE,IAAA,oCAAiB,EAAC,IAAI,CAAC;YAC9B,UAAU,EAAE,IAAI,CAAC,EAAE;SACpB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,GAAG,CAAC,iBAAiB,CAAC,sCAAY,CAAC,gBAAgB,CAAC,CAAC;QACpE,MAAM,IAAA,iCAAc,EAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE7C,IAAI,CAAC;YACH,iEAAiE;YACjE,MAAM,iBAAiB,GAAkB;gBACvC,WAAW,kCAAO,EAAE,KAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,sCAAW,CAAC,gBAAgB,GAAE;aACvF,CAAC;YACF,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;gBAC3B,aAAa,EAAE,iBAAuD;aACvE,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,IAAA,yCAAsB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC7C,CAAC;YAAC,WAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9C,OAAO,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,IAAI,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,GAAuB;;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,sBAAa,CAAC,gBAAgB,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,EAAE,GAAG,IAAA,uCAAY,EAAC,MAAA,IAAI,CAAC,aAAa,mCAAI,IAAI,CAAC,CAAC;QAEpD,mEAAmE;QACnE,2EAA2E;QAC3E,uDAAuD;QACvD,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAEjC,4EAA4E;QAC5E,qEAAqE;QACrE,mEAAmE;QACnE,uEAAuE;QACvE,wEAAwE;QACxE,qEAAqE;QACrE,4CAA4C;QAC5C,IAAI,EAAE,EAAE,CAAC;YACP,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,KAAc,EAAE,EAAX,IAAI,UAAK,EAAE,EAApD,gCAA+C,CAAK,CAAC;YAC3D,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,CAAC;YACR,MAAM,iBAAiB,GAAkB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAC/D,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;gBAC3B,aAAa,EAAE,iBAAuD;aACvE,CAAC,CAAC;QACL,CAAC;QAED,IAAI,EAAE,aAAF,EAAE,uBAAF,EAAE,CAAE,YAAY,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,GAAG,CAAC,iBAAiB,CAAC,sCAAY,CAAC,gBAAgB,CAAC,CAAC;YACpE,MAAM,IAAA,yCAAsB,EAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9C,OAAO,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,IAAI,CAAC;IACzB,CAAC;CACF;AA5KD,0CA4KC"}
@@ -10,8 +10,11 @@ export declare class IdMappingSyncStrategy implements SyncFlowStrategy {
10
10
  private readonly idMappingService;
11
11
  constructor(idMappingService: IdMappingService);
12
12
  processBatch(ctx: IntegrationContext, flow: DataFlow, source: Adapter | null, target: Adapter, records: Record<string, unknown>[], localFilter: FilterSpec | null): Promise<BatchResult>;
13
+ private _claimCreatesForBatch;
14
+ private _releaseFailedClaims;
13
15
  handleDeleteEvent(ctx: IntegrationContext, flow: DataFlow, target: Adapter, recordId: string): Promise<SyncResult>;
14
16
  handleUpsertEvent(ctx: IntegrationContext, flow: DataFlow, source: Adapter | null, target: Adapter, event: MappedEvent): Promise<SyncResult>;
17
+ private _buildLookupQueries;
15
18
  private _resolveRelationshipsBatch;
16
19
  private _resolveRelationshipsOne;
17
20
  private _writeMappingEntries;
@@ -1 +1 @@
1
- {"version":3,"file":"id-mapping-sync.strategy.d.ts","sourceRoot":"","sources":["../../src/services/id-mapping-sync.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,iCAAiC,CAAC;AAC9F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AA+CxD,qBAAa,qBAAsB,YAAW,gBAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,gBAAgB;IAEzD,YAAY,CAChB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClC,WAAW,EAAE,UAAU,GAAG,IAAI,GAC7B,OAAO,CAAC,WAAW,CAAC;IA6HjB,iBAAiB,CACrB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC;IAqDhB,iBAAiB,CACrB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,UAAU,CAAC;YA0FR,0BAA0B;YAkG1B,wBAAwB;YAiFxB,oBAAoB;CA4CnC"}
1
+ {"version":3,"file":"id-mapping-sync.strategy.d.ts","sourceRoot":"","sources":["../../src/services/id-mapping-sync.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,iCAAiC,CAAC;AAC9F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AA+CxD,qBAAa,qBAAsB,YAAW,gBAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,gBAAgB;IAEzD,YAAY,CAChB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClC,WAAW,EAAE,UAAU,GAAG,IAAI,GAC7B,OAAO,CAAC,WAAW,CAAC;YAmKT,qBAAqB;YAyBrB,oBAAoB;IAsB5B,iBAAiB,CACrB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC;IAqDhB,iBAAiB,CACrB,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,UAAU,CAAC;IAoItB,OAAO,CAAC,mBAAmB;YAsCb,0BAA0B;YAmF1B,wBAAwB;YAkExB,oBAAoB;CA4CnC"}
@@ -83,14 +83,37 @@ class IdMappingSyncStrategy {
83
83
  const activeSrcIds = activeSourceIds.filter((_, i) => !skipSet.has(i));
84
84
  if (activePairs.length === 0)
85
85
  return stats;
86
- const isCreate = activePairs.map((p) => p.mapped['__targetUID'] == null);
86
+ // Step 4b: claim creates. The mapping table has no idempotency key on the target write,
87
+ // so concurrent deliveries of the same new record (e.g. a Skedulo INSERT plus a near-instant
88
+ // UPDATE) can both resolve to "no mapping" and both insert — a duplicate. Reserving the
89
+ // UniqueKey before the write serializes them: the winner creates, a loser means another
90
+ // in-flight delivery already owns the create, so we skip it (its change reconciles on the
91
+ // next sync) rather than create a second record.
92
+ const wonUidBySourceId = await this._claimCreatesForBatch(ctx, flow, activePairs, activeSrcIds);
93
+ const keep = [];
94
+ for (let i = 0; i < activePairs.length; i++) {
95
+ const isCreateI = activePairs[i].mapped['__targetUID'] == null;
96
+ if (isCreateI && activeSrcIds[i] && !wonUidBySourceId.has(activeSrcIds[i])) {
97
+ stats.skipped++;
98
+ stats.succeeded--;
99
+ ctx.logger.warn(`id_mapping_create_lost_claim: dataFlowId=${flow.id} sourceId=${activeSrcIds[i]} ` +
100
+ `objectType=${flow.targetObject} — concurrent create won the claim; skipping to avoid a duplicate`);
101
+ continue;
102
+ }
103
+ keep.push(i);
104
+ }
105
+ const dispatchPairs = keep.map((i) => activePairs[i]);
106
+ const dispatchSrcIds = keep.map((i) => activeSrcIds[i]);
107
+ if (dispatchPairs.length === 0)
108
+ return stats;
109
+ const isCreate = dispatchPairs.map((p) => p.mapped['__targetUID'] == null);
87
110
  // Step 5: write to target — adapter performs insert (new) or update (existing).
88
111
  await ((_j = source === null || source === void 0 ? void 0 : source.beforeUpsert) === null || _j === void 0 ? void 0 : _j.call(source, ctx));
89
112
  await ((_k = target.beforeUpsert) === null || _k === void 0 ? void 0 : _k.call(target, ctx));
90
- let outcomes = new Array(activePairs.length);
113
+ let outcomes = new Array(dispatchPairs.length);
91
114
  try {
92
115
  if (typeof target.insert === 'function' || typeof target.update === 'function') {
93
- const { inserts, updates } = splitForDispatch(activePairs);
116
+ const { inserts, updates } = splitForDispatch(dispatchPairs);
94
117
  if (inserts.length > 0) {
95
118
  const insertOutcomes = typeof target.insert === 'function'
96
119
  ? await target.insert(ctx, flow, inserts.map((x) => x.mapped))
@@ -111,15 +134,15 @@ class IdMappingSyncStrategy {
111
134
  else {
112
135
  // Legacy adapters that only implement upsert(): pass the pairs through
113
136
  // verbatim — they read __targetUID themselves and branch internally.
114
- outcomes = await target.upsert(ctx, flow, activePairs.map((p) => p.mapped));
137
+ outcomes = await target.upsert(ctx, flow, dispatchPairs.map((p) => p.mapped));
115
138
  }
116
139
  if (ctx.pipeline)
117
140
  ctx.pipeline.outcomes = outcomes;
118
141
  for (let i = 0; i < outcomes.length; i++) {
119
142
  if (outcomes[i].status !== 'ok') {
120
143
  const message = (_l = outcomes[i].error) !== null && _l !== void 0 ? _l : outcomes[i].status;
121
- captureRecordError(ctx, 'upsert', i, message, (_m = activePairs[i]) === null || _m === void 0 ? void 0 : _m.raw);
122
- ctx.logger.error(`upsert_failed: dataFlowId=${flow.id} recordIndex=${i} status=${outcomes[i].status} error=${message} data=${JSON.stringify((_p = (_o = activePairs[i]) === null || _o === void 0 ? void 0 : _o.raw) !== null && _p !== void 0 ? _p : {})}`);
144
+ captureRecordError(ctx, 'upsert', i, message, (_m = dispatchPairs[i]) === null || _m === void 0 ? void 0 : _m.raw);
145
+ ctx.logger.error(`upsert_failed: dataFlowId=${flow.id} recordIndex=${i} status=${outcomes[i].status} error=${message} data=${JSON.stringify((_p = (_o = dispatchPairs[i]) === null || _o === void 0 ? void 0 : _o.raw) !== null && _p !== void 0 ? _p : {})}`);
123
146
  stats.succeeded--;
124
147
  stats.failed++;
125
148
  }
@@ -129,15 +152,67 @@ class IdMappingSyncStrategy {
129
152
  }
130
153
  catch (err) {
131
154
  const message = err instanceof Error ? err.message : String(err);
132
- captureProcessError(ctx, 'upsert', message, activePairs.length);
133
- stats.failed += activePairs.length;
134
- stats.succeeded -= activePairs.length;
155
+ captureProcessError(ctx, 'upsert', message, dispatchPairs.length);
156
+ stats.failed += dispatchPairs.length;
157
+ stats.succeeded -= dispatchPairs.length;
158
+ // Outcomes can be partial — one dispatch bucket may have landed before the other threw.
159
+ // Finalize any create that actually succeeded (so it isn't re-created as a duplicate next
160
+ // run) and release reservations only for creates with no successful outcome.
161
+ await this._writeMappingEntries(ctx, flow, target, dispatchSrcIds, outcomes, isCreate);
162
+ await this._releaseFailedClaims(ctx, dispatchSrcIds, isCreate, outcomes, wonUidBySourceId);
135
163
  return stats;
136
164
  }
137
- // Step 6: write back — persist new source→target ID pairs for created records.
138
- await this._writeMappingEntries(ctx, flow, target, activeSrcIds, outcomes, isCreate);
165
+ // Release reservations for creates whose target write failed (empty-TargetId rows would
166
+ // otherwise block the record from ever being created again).
167
+ await this._releaseFailedClaims(ctx, dispatchSrcIds, isCreate, outcomes, wonUidBySourceId);
168
+ // Step 6: write back — finalize each won reservation with the real TargetId (upsert on
169
+ // UniqueKey updates the placeholder row written at claim time).
170
+ await this._writeMappingEntries(ctx, flow, target, dispatchSrcIds, outcomes, isCreate);
139
171
  return stats;
140
172
  }
173
+ // Reserve the UniqueKey for every create-classified pair. Returns the reserved-row UID per
174
+ // won sourceId; absence from the map means the claim was lost to a concurrent delivery.
175
+ async _claimCreatesForBatch(ctx, flow, activePairs, activeSrcIds) {
176
+ const claims = activePairs
177
+ .map((p, i) => ({ create: p.mapped['__targetUID'] == null, sourceId: activeSrcIds[i] }))
178
+ .filter((c) => c.create && c.sourceId)
179
+ .map((c) => {
180
+ var _a, _b;
181
+ return ({
182
+ sourceSystem: (_a = flow.sourceSystem) !== null && _a !== void 0 ? _a : '',
183
+ targetSystem: (_b = flow.targetSystem) !== null && _b !== void 0 ? _b : '',
184
+ objectType: flow.targetObject,
185
+ sourceId: c.sourceId,
186
+ dataFlowId: String(flow.id),
187
+ });
188
+ });
189
+ if (claims.length === 0)
190
+ return new Map();
191
+ const { wonUidBySourceId } = await this.idMappingService.claimCreates(ctx, claims);
192
+ return wonUidBySourceId;
193
+ }
194
+ // Delete reserved claim rows for creates with no successful outcome, so a stale empty-TargetId
195
+ // row can't permanently block the record from ever being created. Retried once for symmetry
196
+ // with the mapping write — a transient failure here strands the reservation. (A crash between
197
+ // reserve and this release is still possible; such a row needs manual cleanup or reclaim.)
198
+ async _releaseFailedClaims(ctx, dispatchSrcIds, isCreate, outcomes, wonUidBySourceId) {
199
+ var _a;
200
+ const uids = [];
201
+ for (let i = 0; i < dispatchSrcIds.length; i++) {
202
+ if (!isCreate[i])
203
+ continue;
204
+ if (((_a = outcomes[i]) === null || _a === void 0 ? void 0 : _a.status) === 'ok')
205
+ continue;
206
+ const uid = wonUidBySourceId.get(dispatchSrcIds[i]);
207
+ if (uid)
208
+ uids.push(uid);
209
+ }
210
+ if (uids.length === 0)
211
+ return;
212
+ await retryOnce(() => this.idMappingService.deleteReservations(ctx, uids)).catch((err) => {
213
+ ctx.logger.error(`id_mapping_reservation_release_failed: ${err instanceof Error ? err.message : String(err)} uids=${JSON.stringify(uids)}`);
214
+ });
215
+ }
141
216
  async handleDeleteEvent(ctx, flow, target, recordId) {
142
217
  var _a, _b, _c, _d;
143
218
  const mappingTable = await this.idMappingService.resolve(ctx, [
@@ -183,7 +258,7 @@ class IdMappingSyncStrategy {
183
258
  return { status: 'synced', recordsProcessed: 1 };
184
259
  }
185
260
  async handleUpsertEvent(ctx, flow, source, target, event) {
186
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
261
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
187
262
  // Initialise pipeline data so beforeMap can mutate raw.
188
263
  if (ctx.pipeline) {
189
264
  ctx.pipeline.data = [{ raw: event.payload, mapped: {} }];
@@ -215,35 +290,71 @@ class IdMappingSyncStrategy {
215
290
  await ((_q = target.beforeUpsert) === null || _q === void 0 ? void 0 : _q.call(target, ctx));
216
291
  // Dispatch insert vs update based on resolved __targetUID, mirroring processBatch.
217
292
  const isCreate = mapped['__targetUID'] == null;
218
- let outcomes;
219
- if (isCreate && typeof target.insert === 'function') {
220
- const _x = mapped, { __targetUID: _drop } = _x, payload = __rest(_x, ["__targetUID"]);
221
- outcomes = await target.insert(ctx, flow, [payload]);
293
+ // Claim the create before writing (see processBatch): serialize concurrent deliveries of the
294
+ // same new record via the UniqueKey constraint. A lost claim means another delivery already
295
+ // owns the create — skip rather than insert a duplicate.
296
+ let reservedUid;
297
+ if (isCreate) {
298
+ const { wonUidBySourceId, lostSourceIds } = await this.idMappingService.claimCreates(ctx, [
299
+ {
300
+ sourceSystem: (_r = flow.sourceSystem) !== null && _r !== void 0 ? _r : '',
301
+ targetSystem: (_s = flow.targetSystem) !== null && _s !== void 0 ? _s : '',
302
+ objectType: flow.targetObject,
303
+ sourceId,
304
+ dataFlowId: String(flow.id),
305
+ },
306
+ ]);
307
+ if (lostSourceIds.length > 0) {
308
+ ctx.logger.warn(`id_mapping_create_lost_claim: dataFlowId=${flow.id} sourceId=${sourceId} ` +
309
+ `objectType=${flow.targetObject} — concurrent create won the claim; skipping to avoid a duplicate`);
310
+ return { status: 'skipped', recordsProcessed: 0, reason: 'concurrent_create' };
311
+ }
312
+ reservedUid = wonUidBySourceId.get(sourceId);
222
313
  }
223
- else if (!isCreate && typeof target.update === 'function') {
224
- outcomes = await target.update(ctx, flow, [mapped]);
314
+ const releaseReservation = async () => {
315
+ if (isCreate && reservedUid) {
316
+ await retryOnce(() => this.idMappingService.deleteReservations(ctx, [reservedUid])).catch((err) => ctx.logger.error(`id_mapping_reservation_release_failed: sourceId=${sourceId} ` +
317
+ `uid=${reservedUid} error=${err instanceof Error ? err.message : String(err)}`));
318
+ }
319
+ };
320
+ let outcomes;
321
+ try {
322
+ if (isCreate && typeof target.insert === 'function') {
323
+ const _z = mapped, { __targetUID: _drop } = _z, payload = __rest(_z, ["__targetUID"]);
324
+ outcomes = await target.insert(ctx, flow, [payload]);
325
+ }
326
+ else if (!isCreate && typeof target.update === 'function') {
327
+ outcomes = await target.update(ctx, flow, [mapped]);
328
+ }
329
+ else {
330
+ // Legacy path
331
+ outcomes = await target.upsert(ctx, flow, [mapped]);
332
+ }
225
333
  }
226
- else {
227
- // Legacy path
228
- outcomes = await target.upsert(ctx, flow, [mapped]);
334
+ catch (err) {
335
+ // Release the reservation so a stale empty-TargetId row can't permanently block re-creation.
336
+ await releaseReservation();
337
+ throw err;
229
338
  }
230
339
  const outcome = outcomes[0];
231
340
  if (ctx.pipeline)
232
341
  ctx.pipeline.outcomes = outcomes;
233
- await ((_r = source === null || source === void 0 ? void 0 : source.afterUpsert) === null || _r === void 0 ? void 0 : _r.call(source, ctx));
234
- await ((_s = target.afterUpsert) === null || _s === void 0 ? void 0 : _s.call(target, ctx));
342
+ await ((_t = source === null || source === void 0 ? void 0 : source.afterUpsert) === null || _t === void 0 ? void 0 : _t.call(source, ctx));
343
+ await ((_u = target.afterUpsert) === null || _u === void 0 ? void 0 : _u.call(target, ctx));
235
344
  if (outcome.status === 'precondition') {
236
- ctx.logger.error(`push_precondition: dataFlowId=${flow.id} recordId=${event.recordId} error=${(_t = outcome.error) !== null && _t !== void 0 ? _t : 'unknown'}`);
345
+ await releaseReservation();
346
+ ctx.logger.error(`push_precondition: dataFlowId=${flow.id} recordId=${event.recordId} error=${(_v = outcome.error) !== null && _v !== void 0 ? _v : 'unknown'}`);
237
347
  return {
238
348
  status: 'precondition_failed',
239
349
  recordsProcessed: 0,
240
- error: (_u = outcome.error) !== null && _u !== void 0 ? _u : outcome.status,
350
+ error: (_w = outcome.error) !== null && _w !== void 0 ? _w : outcome.status,
241
351
  };
242
352
  }
243
353
  if (outcome.status !== 'ok') {
354
+ await releaseReservation();
244
355
  ctx.logger.error(`push_failed: dataFlowId=${flow.id} recordId=${event.recordId} ` +
245
- `class=${outcome.status} error=${(_v = outcome.error) !== null && _v !== void 0 ? _v : 'unknown'}`);
246
- return { status: 'failed', recordsProcessed: 0, error: (_w = outcome.error) !== null && _w !== void 0 ? _w : outcome.status };
356
+ `class=${outcome.status} error=${(_x = outcome.error) !== null && _x !== void 0 ? _x : 'unknown'}`);
357
+ return { status: 'failed', recordsProcessed: 0, error: (_y = outcome.error) !== null && _y !== void 0 ? _y : outcome.status };
247
358
  }
248
359
  // Step 7: persist new source→target ID pair for a created record.
249
360
  if (isCreate) {
@@ -251,35 +362,45 @@ class IdMappingSyncStrategy {
251
362
  }
252
363
  return { status: 'synced', recordsProcessed: 1 };
253
364
  }
254
- // Resolves primary identity and all relationship fields for a batch of pairs.
255
- // Mutates pairs[i].mapped in place (sets __targetUID, substitutes lookup UIDs).
256
- // Mutates stats in place (filtered/failed counts for onMissing handling).
257
- // Returns the set of pair indices to skip, or null if the mapping table query failed.
258
- async _resolveRelationshipsBatch(ctx, flow, pairs, activeSourceIds, stats) {
259
- var _a, _b, _c, _d, _e, _f;
365
+ // Collect relationship-lookup ids grouped by lookup object — shared by the batch and
366
+ // event paths so both build identical resolveLookups() queries (an event is a batch of one).
367
+ _buildLookupQueries(flow, mappedRecords) {
368
+ var _a, _b, _c, _d;
260
369
  const lookupMappings = ((_b = (_a = flow.mappingConfig) === null || _a === void 0 ? void 0 : _a.mappings) !== null && _b !== void 0 ? _b : []).filter((m) => m.lookup);
261
- // Collect all lookup source IDs grouped by object type for a single batch query.
262
- const lookupIdsByObject = new Map();
370
+ const idsByObject = new Map();
263
371
  for (const mapping of lookupMappings) {
264
372
  const rule = mapping.lookup;
265
- const values = pairs
266
- .map((p) => (p.mapped[mapping.target] != null ? String(p.mapped[mapping.target]) : ''))
267
- .filter((v) => v.length > 0);
268
- if (values.length === 0)
269
- continue;
270
- const existing = (_c = lookupIdsByObject.get(rule.object)) !== null && _c !== void 0 ? _c : new Set();
271
- for (const v of values)
272
- existing.add(v);
273
- lookupIdsByObject.set(rule.object, existing);
373
+ for (const mapped of mappedRecords) {
374
+ const v = mapped[mapping.target] != null ? String(mapped[mapping.target]) : '';
375
+ if (!v)
376
+ continue;
377
+ let set = idsByObject.get(rule.object);
378
+ if (!set) {
379
+ set = new Set();
380
+ idsByObject.set(rule.object, set);
381
+ }
382
+ set.add(v);
383
+ }
274
384
  }
275
- const sourceSystem = (_d = flow.sourceSystem) !== null && _d !== void 0 ? _d : '';
276
- const targetSystem = (_e = flow.targetSystem) !== null && _e !== void 0 ? _e : '';
277
- const lookupQueries = [...lookupIdsByObject.entries()].map(([objectType, ids]) => ({
385
+ const sourceSystem = (_c = flow.sourceSystem) !== null && _c !== void 0 ? _c : '';
386
+ const targetSystem = (_d = flow.targetSystem) !== null && _d !== void 0 ? _d : '';
387
+ return [...idsByObject.entries()].map(([objectType, ids]) => ({
278
388
  sourceSystem,
279
389
  targetSystem,
280
390
  objectType,
281
391
  sourceIds: [...ids],
282
392
  }));
393
+ }
394
+ // Resolves primary identity and all relationship fields for a batch of pairs.
395
+ // Mutates pairs[i].mapped in place (sets __targetUID, substitutes lookup UIDs).
396
+ // Mutates stats in place (filtered/failed counts for onMissing handling).
397
+ // Returns the set of pair indices to skip, or null if the mapping table query failed.
398
+ async _resolveRelationshipsBatch(ctx, flow, pairs, activeSourceIds, stats) {
399
+ var _a, _b, _c, _d, _e;
400
+ const lookupMappings = ((_b = (_a = flow.mappingConfig) === null || _a === void 0 ? void 0 : _a.mappings) !== null && _b !== void 0 ? _b : []).filter((m) => m.lookup);
401
+ const sourceSystem = (_c = flow.sourceSystem) !== null && _c !== void 0 ? _c : '';
402
+ const targetSystem = (_d = flow.targetSystem) !== null && _d !== void 0 ? _d : '';
403
+ const lookupQueries = this._buildLookupQueries(flow, pairs.map((p) => p.mapped));
283
404
  // Primary identity stays own-direction (it decides create vs. update); relationship
284
405
  // lookups resolve direction-agnostically so a relationship synced the other way still
285
406
  // resolves. Separate maps so a lookup never overwrites a primary-identity key.
@@ -305,7 +426,7 @@ class IdMappingSyncStrategy {
305
426
  // SkeduloAdapter uses key presence to switch insert vs. upsert-by-UID.
306
427
  for (let i = 0; i < pairs.length; i++) {
307
428
  pairs[i].mapped['__targetUID'] =
308
- (_f = primaryTable.get(`${flow.targetObject}:${activeSourceIds[i]}`)) !== null && _f !== void 0 ? _f : null;
429
+ (_e = primaryTable.get(`${flow.targetObject}:${activeSourceIds[i]}`)) !== null && _e !== void 0 ? _e : null;
309
430
  }
310
431
  // Apply lookup (relationship) field resolution with onMissing handling.
311
432
  const skipSet = new Set();
@@ -349,19 +470,8 @@ class IdMappingSyncStrategy {
349
470
  const lookupMappings = ((_b = (_a = flow.mappingConfig) === null || _a === void 0 ? void 0 : _a.mappings) !== null && _b !== void 0 ? _b : []).filter((m) => m.lookup);
350
471
  const sourceSystem = (_c = flow.sourceSystem) !== null && _c !== void 0 ? _c : '';
351
472
  const targetSystem = (_d = flow.targetSystem) !== null && _d !== void 0 ? _d : '';
352
- const lookupQueries = [];
353
- for (const mapping of lookupMappings) {
354
- const rule = mapping.lookup;
355
- const rawValue = mapped[mapping.target] != null ? String(mapped[mapping.target]) : '';
356
- if (!rawValue)
357
- continue;
358
- lookupQueries.push({
359
- sourceSystem,
360
- targetSystem,
361
- objectType: rule.object,
362
- sourceIds: [rawValue],
363
- });
364
- }
473
+ // Same query-builder as the batch path — a single event record is a batch of one.
474
+ const lookupQueries = this._buildLookupQueries(flow, [mapped]);
365
475
  // Primary identity own-direction; relationship lookups direction-agnostic (see batch path).
366
476
  // Convert resolve failures into a controlled terminal result — handleUpsertEvent has no
367
477
  // outer catch, so an uncaught throw here would fail the whole event less gracefully than batch.