@open-mercato/core 0.6.8-develop.7027.1.c9b5e86524 → 0.6.8-develop.7030.1.b84302d973
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/dist/modules/data_sync/lib/sync-engine.js +72 -6
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +3 -0
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/data_sync/AGENTS.md +1 -1
- package/src/modules/data_sync/lib/adapter.ts +36 -0
- package/src/modules/data_sync/lib/sync-engine.ts +113 -5
- package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
- package/src/modules/wms/commands/inventory-actions.ts +3 -0
|
@@ -50,6 +50,9 @@ function applyExportCounters(batch) {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
const HEARTBEAT_TICK_MS = STALE_JOB_TIMEOUT_SECONDS * 1e3 / 4;
|
|
53
|
+
function isAbortError(error) {
|
|
54
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
55
|
+
}
|
|
53
56
|
async function* withHeartbeat(source, tick, intervalMs) {
|
|
54
57
|
const iterator = source[Symbol.asyncIterator]();
|
|
55
58
|
try {
|
|
@@ -122,6 +125,25 @@ function createSyncEngine(deps) {
|
|
|
122
125
|
});
|
|
123
126
|
};
|
|
124
127
|
}
|
|
128
|
+
function makeCancellationTick(progressJobId, scope, controller) {
|
|
129
|
+
if (!progressJobId) return () => {
|
|
130
|
+
};
|
|
131
|
+
let inFlight = false;
|
|
132
|
+
return () => {
|
|
133
|
+
if (inFlight || controller.signal.aborted) return;
|
|
134
|
+
inFlight = true;
|
|
135
|
+
progressService.isCancellationRequested(progressJobId, scope.tenantId, scope.organizationId).then((cancelled) => {
|
|
136
|
+
if (cancelled) controller.abort();
|
|
137
|
+
}).catch((error) => {
|
|
138
|
+
logger.warn("Cancellation poll failed", {
|
|
139
|
+
progressJobId,
|
|
140
|
+
error: error instanceof Error ? error.message : String(error)
|
|
141
|
+
});
|
|
142
|
+
}).finally(() => {
|
|
143
|
+
inFlight = false;
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
}
|
|
125
147
|
async function refreshCoverageSnapshots(entityTypes, scope) {
|
|
126
148
|
if (!entityTypes || entityTypes.length === 0) return;
|
|
127
149
|
await Promise.allSettled(
|
|
@@ -424,8 +446,12 @@ function createSyncEngine(deps) {
|
|
|
424
446
|
let processedCount = await seedProcessedCount(run.progressJobId, scope);
|
|
425
447
|
let totalCount = null;
|
|
426
448
|
let committedBatches = activeRun.batchesCompleted ?? 0;
|
|
449
|
+
let streamReportedDone = false;
|
|
427
450
|
const runTrace = captureTelemetryTrace();
|
|
428
451
|
const spanAttributes = runSpanAttributes(run, providerKey, scope);
|
|
452
|
+
const cancellation = new AbortController();
|
|
453
|
+
const heartbeat = makeHeartbeatTick(run.progressJobId, scope);
|
|
454
|
+
const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation);
|
|
429
455
|
try {
|
|
430
456
|
const streamResult = await forEachBatch(
|
|
431
457
|
withHeartbeat(
|
|
@@ -437,9 +463,18 @@ function createSyncEngine(deps) {
|
|
|
437
463
|
mapping,
|
|
438
464
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
439
465
|
runId: run.id,
|
|
440
|
-
parameters: run.parameters ?? {}
|
|
466
|
+
parameters: run.parameters ?? {},
|
|
467
|
+
signal: cancellation.signal
|
|
441
468
|
}),
|
|
442
|
-
|
|
469
|
+
// `finally` so a synchronous throw from the heartbeat cannot also stop cancellation
|
|
470
|
+
// from being observed for the rest of the run.
|
|
471
|
+
() => {
|
|
472
|
+
try {
|
|
473
|
+
heartbeat();
|
|
474
|
+
} finally {
|
|
475
|
+
pollCancellation();
|
|
476
|
+
}
|
|
477
|
+
},
|
|
443
478
|
HEARTBEAT_TICK_MS
|
|
444
479
|
),
|
|
445
480
|
{
|
|
@@ -453,7 +488,7 @@ function createSyncEngine(deps) {
|
|
|
453
488
|
"data_sync.batch_index": batch.batchIndex,
|
|
454
489
|
"data_sync.batch_size": batch.items.length
|
|
455
490
|
});
|
|
456
|
-
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
491
|
+
if (cancellation.signal.aborted || run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
457
492
|
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
458
493
|
return "stop";
|
|
459
494
|
}
|
|
@@ -479,6 +514,7 @@ function createSyncEngine(deps) {
|
|
|
479
514
|
{ expectedBatchesCompleted: committedBatches, persistSharedCursor }
|
|
480
515
|
);
|
|
481
516
|
committedBatches += 1;
|
|
517
|
+
streamReportedDone = batch.hasMore === false;
|
|
482
518
|
await updateProgress(run.progressJobId, processedCount, totalCount, scope);
|
|
483
519
|
await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope);
|
|
484
520
|
await logImportItemFailures(run.id, run.integrationId, batch.items, scope);
|
|
@@ -510,6 +546,10 @@ function createSyncEngine(deps) {
|
|
|
510
546
|
});
|
|
511
547
|
return;
|
|
512
548
|
}
|
|
549
|
+
if (cancellation.signal.aborted && isAbortError(error)) {
|
|
550
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
513
553
|
const message = error instanceof Error ? error.message : "Sync import failed";
|
|
514
554
|
await integrationLogService.write(
|
|
515
555
|
{
|
|
@@ -523,6 +563,10 @@ function createSyncEngine(deps) {
|
|
|
523
563
|
await finalizeRun(run.id, "failed", scope, message, operationalTelemetry);
|
|
524
564
|
return;
|
|
525
565
|
}
|
|
566
|
+
if (cancellation.signal.aborted && !streamReportedDone) {
|
|
567
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
526
570
|
await finalizeRun(run.id, "completed", scope, void 0, operationalTelemetry);
|
|
527
571
|
},
|
|
528
572
|
async runExport(runId, batchSize, scope) {
|
|
@@ -596,8 +640,12 @@ function createSyncEngine(deps) {
|
|
|
596
640
|
const mapping = await resolveMapping(adapter, run.entityType, scope);
|
|
597
641
|
let processedCount = await seedProcessedCount(run.progressJobId, scope);
|
|
598
642
|
let committedBatches = activeRun.batchesCompleted ?? 0;
|
|
643
|
+
let streamReportedDone = false;
|
|
599
644
|
const runTrace = captureTelemetryTrace();
|
|
600
645
|
const spanAttributes = runSpanAttributes(run, providerKey, scope);
|
|
646
|
+
const cancellation = new AbortController();
|
|
647
|
+
const heartbeat = makeHeartbeatTick(run.progressJobId, scope);
|
|
648
|
+
const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation);
|
|
601
649
|
try {
|
|
602
650
|
const streamResult = await forEachBatch(
|
|
603
651
|
withHeartbeat(
|
|
@@ -609,9 +657,18 @@ function createSyncEngine(deps) {
|
|
|
609
657
|
mapping,
|
|
610
658
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
611
659
|
runId: run.id,
|
|
612
|
-
parameters: run.parameters ?? {}
|
|
660
|
+
parameters: run.parameters ?? {},
|
|
661
|
+
signal: cancellation.signal
|
|
613
662
|
}),
|
|
614
|
-
|
|
663
|
+
// `finally` so a synchronous throw from the heartbeat cannot also stop cancellation
|
|
664
|
+
// from being observed for the rest of the run.
|
|
665
|
+
() => {
|
|
666
|
+
try {
|
|
667
|
+
heartbeat();
|
|
668
|
+
} finally {
|
|
669
|
+
pollCancellation();
|
|
670
|
+
}
|
|
671
|
+
},
|
|
615
672
|
HEARTBEAT_TICK_MS
|
|
616
673
|
),
|
|
617
674
|
{
|
|
@@ -625,7 +682,7 @@ function createSyncEngine(deps) {
|
|
|
625
682
|
"data_sync.batch_index": batch.batchIndex,
|
|
626
683
|
"data_sync.batch_size": batch.results.length
|
|
627
684
|
});
|
|
628
|
-
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
685
|
+
if (cancellation.signal.aborted || run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
629
686
|
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
630
687
|
return "stop";
|
|
631
688
|
}
|
|
@@ -651,6 +708,7 @@ function createSyncEngine(deps) {
|
|
|
651
708
|
{ expectedBatchesCompleted: committedBatches, persistSharedCursor }
|
|
652
709
|
);
|
|
653
710
|
committedBatches += 1;
|
|
711
|
+
streamReportedDone = batch.hasMore === false;
|
|
654
712
|
await updateProgress(run.progressJobId, processedCount, null, scope);
|
|
655
713
|
await logExportItemFailures(run.id, run.integrationId, batch.results, scope);
|
|
656
714
|
await writeOperationalLog({
|
|
@@ -680,6 +738,10 @@ function createSyncEngine(deps) {
|
|
|
680
738
|
});
|
|
681
739
|
return;
|
|
682
740
|
}
|
|
741
|
+
if (cancellation.signal.aborted && isAbortError(error)) {
|
|
742
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
683
745
|
const message = error instanceof Error ? error.message : "Sync export failed";
|
|
684
746
|
await integrationLogService.write(
|
|
685
747
|
{
|
|
@@ -693,6 +755,10 @@ function createSyncEngine(deps) {
|
|
|
693
755
|
await finalizeRun(run.id, "failed", scope, message, operationalTelemetry);
|
|
694
756
|
return;
|
|
695
757
|
}
|
|
758
|
+
if (cancellation.signal.aborted && !streamReportedDone) {
|
|
759
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
696
762
|
await finalizeRun(run.id, "completed", scope, void 0, operationalTelemetry);
|
|
697
763
|
}
|
|
698
764
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/lib/sync-engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport { STALE_JOB_TIMEOUT_SECONDS } from '../../progress/lib/progressService'\nimport { refreshCoverageSnapshot } from '../../query_index/lib/coverage'\nimport { emitDataSyncEvent } from '../events'\nimport type { DataSyncAdapter, DataMapping, ExportBatch, ImportBatch, RunParameterValue } from './adapter'\nimport { getDataSyncAdapter, resolveProviderKey } from './adapter-registry'\nimport type { SyncRunService } from './sync-run-service'\nimport { SyncRunOwnershipConflictError } from './sync-run-service'\nimport { forEachBatch } from './batch-stream'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n captureTelemetryTrace,\n type TelemetrySpanAttributes,\n} from '@open-mercato/shared/lib/telemetry/runtime'\nimport type { SyncRun } from '../data/entities'\n\nconst logger = createLogger('data_sync').child({ component: 'sync-engine' })\n\ntype RunParameters = Record<string, RunParameterValue>\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n userId?: string | null\n}\n\ntype EngineDeps = {\n em: EntityManager\n syncRunService: SyncRunService\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n integrationStateService?: IntegrationStateService\n progressService: ProgressService\n}\n\n/** Repeated on every batch span so a rooted batch trace identifies its run on its own. */\nfunction runSpanAttributes(run: SyncRun, providerKey: string, scope: SyncScope): TelemetrySpanAttributes {\n return {\n 'data_sync.run_id': run.id,\n 'data_sync.integration_id': run.integrationId,\n 'data_sync.provider_key': providerKey,\n 'data_sync.entity_type': run.entityType,\n 'data_sync.direction': run.direction,\n 'om.tenant_id': scope.tenantId,\n 'om.organization_id': scope.organizationId,\n }\n}\n\nfunction applyImportCounters(batch: ImportBatch): Pick<Required<SyncCounterDelta>, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount'> {\n let createdCount = 0\n let updatedCount = 0\n let skippedCount = 0\n let failedCount = 0\n\n for (const item of batch.items) {\n if (item.action === 'create') createdCount += 1\n else if (item.action === 'update') updatedCount += 1\n else if (item.action === 'failed') failedCount += 1\n else skippedCount += 1\n }\n\n return { createdCount, updatedCount, skippedCount, failedCount }\n}\n\ntype SyncCounterDelta = {\n createdCount?: number\n updatedCount?: number\n skippedCount?: number\n failedCount?: number\n processedCount: number\n}\n\nfunction applyExportCounters(batch: ExportBatch): SyncCounterDelta {\n let failedCount = 0\n let skippedCount = 0\n let updatedCount = 0\n\n for (const result of batch.results) {\n if (result.status === 'error') failedCount += 1\n else if (result.status === 'skipped') skippedCount += 1\n else updatedCount += 1\n }\n\n return {\n failedCount,\n skippedCount,\n updatedCount,\n processedCount: batch.results.length,\n }\n}\n\n// Adapter batches can legitimately outlast the stale-job sweep window (slow upstream\n// APIs), so the engine must heartbeat while a batch is still being produced.\nconst HEARTBEAT_TICK_MS = (STALE_JOB_TIMEOUT_SECONDS * 1000) / 4\n\n// Runs `tick` on an interval only while the source iterator is pending, so heartbeats\n// stop the moment the producer dies and genuinely stale jobs still get swept. The outer\n// finally closes the adapter generator on early exits (cancellation, ownership conflict).\nasync function* withHeartbeat<T>(source: AsyncIterable<T>, tick: () => void, intervalMs: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n while (true) {\n const timer = setInterval(tick, intervalMs)\n let result: IteratorResult<T>\n try {\n result = await iterator.next()\n } finally {\n clearInterval(timer)\n }\n if (result.done) return\n yield result.value\n }\n } finally {\n await iterator.return?.()\n }\n}\n\nexport function createSyncEngine(deps: EngineDeps) {\n const { syncRunService, integrationCredentialsService, integrationLogService, integrationStateService, progressService } = deps\n\n async function resolveMapping(adapter: DataSyncAdapter, entityType: string, scope: SyncScope): Promise<DataMapping> {\n return adapter.getMapping({\n entityType,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })\n }\n\n async function updateProgress(progressJobId: string | null | undefined, processedCount: number, totalCount: number | null, scope: SyncScope): Promise<void> {\n if (!progressJobId) return\n\n await progressService.updateProgress(\n progressJobId,\n {\n processedCount,\n totalCount: totalCount ?? undefined,\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n\n // On redelivery the progress counter must resume where the last delivery left off the\n // same way committedBatches does \u2014 updateProgress writes absolute counts, so starting at\n // zero would regress the visible count. The progress job's own processedCount is the only\n // persisted value already in the engine's unit: `batch.processedCount ?? items.length`,\n // i.e. source records. The run's created/updated/skipped/failed counters count emitted\n // items, which adapters may explode several-per-source-record (Akeneo yields a product\n // plus its variants), so seeding from them would overshoot the total and pin the bar.\n async function seedProcessedCount(progressJobId: string | null | undefined, scope: SyncScope): Promise<number> {\n if (!progressJobId) return 0\n const job = await progressService.getJob(progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n return job?.processedCount ?? 0\n }\n\n function makeHeartbeatTick(progressJobId: string | null | undefined, scope: SyncScope): () => void {\n const touchJobHeartbeat = progressService.touchJobHeartbeat?.bind(progressService)\n if (!progressJobId || !touchJobHeartbeat) return () => {}\n let inFlight = false\n return () => {\n if (inFlight) return\n inFlight = true\n touchJobHeartbeat(progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n .catch((error) => {\n logger.warn('Progress heartbeat failed', {\n progressJobId,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n .finally(() => {\n inFlight = false\n })\n }\n }\n\n async function refreshCoverageSnapshots(entityTypes: string[] | undefined, scope: SyncScope): Promise<void> {\n if (!entityTypes || entityTypes.length === 0) return\n\n await Promise.allSettled(\n Array.from(new Set(entityTypes.filter((value) => typeof value === 'string' && value.trim().length > 0)))\n .map((entityType) => refreshCoverageSnapshot(deps.em, {\n entityType,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })),\n )\n }\n\n async function logImportItemFailures(\n runId: string,\n integrationId: string,\n items: ImportBatch['items'],\n scope: SyncScope,\n ): Promise<void> {\n const failedItems = items.filter((item) => item.action === 'failed')\n for (const item of failedItems) {\n const errorMessage = typeof item.data.errorMessage === 'string' && item.data.errorMessage.trim().length > 0\n ? item.data.errorMessage.trim()\n : 'Import item failed'\n const sourceProductUuid = typeof item.data.sourceProductUuid === 'string' && item.data.sourceProductUuid.trim().length > 0\n ? item.data.sourceProductUuid.trim()\n : null\n const sourceIdentifier = typeof item.data.sourceIdentifier === 'string' && item.data.sourceIdentifier.trim().length > 0\n ? item.data.sourceIdentifier.trim()\n : null\n const message = [\n `Failed to import item ${item.externalId}`,\n sourceProductUuid ? `(uuid: ${sourceProductUuid})` : null,\n sourceIdentifier ? `(identifier: ${sourceIdentifier})` : null,\n `: ${errorMessage}`,\n ].filter((part) => part !== null).join(' ')\n\n await integrationLogService.write(\n {\n integrationId,\n runId,\n level: 'error',\n message,\n payload: item.data,\n },\n scope,\n )\n }\n }\n\n async function logExportItemFailures(\n runId: string,\n integrationId: string,\n results: ExportBatch['results'],\n scope: SyncScope,\n ): Promise<void> {\n const failedResults = results.filter((result) => result.status === 'error' && result.error)\n for (const result of failedResults) {\n const label = result.externalId ? `${result.externalId} (id: ${result.localId})` : result.localId\n const errorMessage = result.error!.split('\\n')[0]\n const message = `Failed to export item ${label}: ${errorMessage}`\n\n await integrationLogService.write(\n {\n integrationId,\n runId,\n level: 'error',\n message,\n payload: { kind: 'export-item-failure', summary: result.error },\n },\n scope,\n )\n }\n }\n\n async function writeOperationalLog(params: {\n integrationId: string\n runId: string\n level: 'info' | 'warn' | 'error'\n message: string\n scope: SyncScope\n enabled: boolean\n payload?: Record<string, unknown>\n }): Promise<void> {\n if (!params.enabled) return\n\n await integrationLogService.write(\n {\n integrationId: params.integrationId,\n runId: params.runId,\n level: params.level,\n message: params.message,\n payload: params.payload,\n },\n params.scope,\n )\n }\n\n async function updateOperationalState(params: {\n integrationId: string\n status: 'healthy' | 'degraded' | 'unhealthy'\n scope: SyncScope\n enabled: boolean\n }): Promise<void> {\n if (!params.enabled || !integrationStateService) return\n\n await integrationStateService.upsert(\n params.integrationId,\n {\n lastHealthStatus: params.status,\n lastHealthCheckedAt: new Date(),\n },\n params.scope,\n )\n }\n\n async function finalizeRun(\n runId: string,\n status: 'completed' | 'failed' | 'cancelled',\n scope: SyncScope,\n error?: string,\n operationalTelemetry = false,\n ): Promise<void> {\n const existingRun = await syncRunService.getRun(runId, scope)\n const alreadyFinalizedWithSameStatus = existingRun?.status === status\n && (status === 'completed' || status === 'failed' || status === 'cancelled')\n\n const run = await syncRunService.markStatus(runId, status, scope, error)\n if (!run) return\n\n if (alreadyFinalizedWithSameStatus) {\n return\n }\n\n if (run.status !== status) {\n // `markStatus` refuses a terminal -> different-terminal transition and\n // returns the row unchanged, so the run is already finished under another\n // delivery of this job. Everything below \u2014 the progress job, the\n // operational log and the lifecycle event \u2014 would describe the wrong\n // outcome, and `data_sync.run.failed` is dispatched to tenant webhooks.\n // A displaced worker stays silent instead.\n logger.warn('Skipping finalization of a sync run another worker already finalized', {\n runId,\n requestedStatus: status,\n actualStatus: run.status,\n })\n return\n }\n\n if (run.progressJobId) {\n if (status === 'completed') {\n await progressService.completeJob(\n run.progressJobId,\n {\n resultSummary: {\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'failed') {\n await progressService.failJob(\n run.progressJobId,\n {\n errorMessage: error ?? 'Sync run failed',\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'cancelled') {\n await progressService.markCancelled(\n run.progressJobId,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n }\n\n if (status === 'completed') {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'healthy',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run completed',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'completed',\n summary: `Sync completed with ${run.createdCount} created, ${run.updatedCount} updated, ${run.failedCount} failed.`,\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n })\n } else if (status === 'cancelled') {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'warn',\n message: 'Sync run cancelled',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'cancelled',\n summary: 'The sync run was cancelled before completion.',\n },\n })\n } else {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'unhealthy',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message: error ?? 'Sync run failed',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'failed',\n summary: error ?? 'The sync run failed.',\n },\n })\n }\n\n if (status === 'completed') {\n await emitDataSyncEvent('data_sync.run.completed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n if (status === 'cancelled') {\n await emitDataSyncEvent('data_sync.run.cancelled', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n await emitDataSyncEvent('data_sync.run.failed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n error: error ?? null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n\n return {\n async runImport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n logger.warn('Skipping stale import job for missing run', { runId })\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamImport) {\n throw new Error(`No import adapter registered for provider ${providerKey}`)\n }\n const operationalTelemetry = adapter.operationalTelemetry === true\n const persistSharedCursor = adapter.persistsSharedCursor?.(run.entityType) ?? true\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n // A run already `running` means a stalled job was redelivered, so this is\n // a resume rather than a first start. Consumers see one `started` event per\n // delivery either way; the flag is what lets them tell the two apart.\n const resumed = run.status === 'running'\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n resumed,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run started',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Import run started for ${run.entityType}.`,\n entityType: run.entityType,\n direction: run.direction,\n },\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = await seedProcessedCount(run.progressJobId, scope)\n let totalCount: number | null = null\n let committedBatches = activeRun.batchesCompleted ?? 0\n // Captured while the triggering job's span is still the active one, so\n // every rooted batch trace can link back to it.\n const runTrace = captureTelemetryTrace()\n const spanAttributes = runSpanAttributes(run, providerKey, scope)\n\n try {\n const streamResult = await forEachBatch(\n withHeartbeat(\n adapter.streamImport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n runId: run.id,\n parameters: (run.parameters ?? {}) as RunParameters,\n }),\n makeHeartbeatTick(run.progressJobId, scope),\n HEARTBEAT_TICK_MS,\n ),\n {\n spanName: 'data_sync.import.batch',\n drainSpanName: 'data_sync.import.drain',\n attributes: spanAttributes,\n linkTo: runTrace,\n },\n async (batch, span) => {\n span.setAttributes({\n 'data_sync.batch_index': batch.batchIndex,\n 'data_sync.batch_size': batch.items.length,\n })\n\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return 'stop'\n }\n\n const delta = applyImportCounters(batch)\n const processedBatchCount = batch.processedCount ?? batch.items.length\n processedCount += processedBatchCount\n totalCount = batch.totalEstimate ?? totalCount\n\n span.setAttributes({\n 'data_sync.processed_count': processedBatchCount,\n 'data_sync.created_count': delta.createdCount,\n 'data_sync.updated_count': delta.updatedCount,\n 'data_sync.skipped_count': delta.skippedCount,\n 'data_sync.failed_count': delta.failedCount,\n })\n\n await syncRunService.commitBatchProgress(\n run.id,\n {\n ...delta,\n batchesCompleted: 1,\n },\n batch.cursor,\n scope,\n { expectedBatchesCompleted: committedBatches, persistSharedCursor },\n )\n committedBatches += 1\n\n await updateProgress(run.progressJobId, processedCount, totalCount, scope)\n await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope)\n await logImportItemFailures(run.id, run.integrationId, batch.items, scope)\n\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: batch.message?.trim().length\n ? batch.message.trim()\n : `Processed import batch ${batch.batchIndex}`,\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Processed ${processedCount}${totalCount ? ` of ${totalCount}` : ''} rows so far.`,\n processedCount,\n batchSize: batch.items.length,\n processedBatchCount,\n cursor: batch.cursor,\n },\n })\n\n return 'continue'\n },\n )\n if (streamResult === 'stopped') return\n } catch (error) {\n if (error instanceof SyncRunOwnershipConflictError) {\n logger.warn('Yielding import run to a concurrent worker that already advanced it', {\n runId: run.id,\n expectedBatchesCompleted: error.expectedBatchesCompleted,\n })\n return\n }\n const message = error instanceof Error ? error.message : 'Sync import failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)\n },\n\n async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n logger.warn('Skipping stale export job for missing run', { runId })\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamExport) {\n throw new Error(`No export adapter registered for provider ${providerKey}`)\n }\n const operationalTelemetry = adapter.operationalTelemetry === true\n const persistSharedCursor = adapter.persistsSharedCursor?.(run.entityType) ?? true\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n // A run already `running` means a stalled job was redelivered, so this is\n // a resume rather than a first start. Consumers see one `started` event per\n // delivery either way; the flag is what lets them tell the two apart.\n const resumed = run.status === 'running'\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n resumed,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run started',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Export run started for ${run.entityType}.`,\n entityType: run.entityType,\n direction: run.direction,\n },\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = await seedProcessedCount(run.progressJobId, scope)\n let committedBatches = activeRun.batchesCompleted ?? 0\n // Captured while the triggering job's span is still the active one, so\n // every rooted batch trace can link back to it.\n const runTrace = captureTelemetryTrace()\n const spanAttributes = runSpanAttributes(run, providerKey, scope)\n\n try {\n const streamResult = await forEachBatch(\n withHeartbeat(\n adapter.streamExport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n runId: run.id,\n parameters: (run.parameters ?? {}) as RunParameters,\n }),\n makeHeartbeatTick(run.progressJobId, scope),\n HEARTBEAT_TICK_MS,\n ),\n {\n spanName: 'data_sync.export.batch',\n drainSpanName: 'data_sync.export.drain',\n attributes: spanAttributes,\n linkTo: runTrace,\n },\n async (batch, span) => {\n span.setAttributes({\n 'data_sync.batch_index': batch.batchIndex,\n 'data_sync.batch_size': batch.results.length,\n })\n\n if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return 'stop'\n }\n\n const delta = applyExportCounters(batch)\n processedCount += delta.processedCount\n\n span.setAttributes({\n 'data_sync.processed_count': delta.processedCount,\n 'data_sync.updated_count': delta.updatedCount,\n 'data_sync.skipped_count': delta.skippedCount,\n 'data_sync.failed_count': delta.failedCount,\n })\n\n await syncRunService.commitBatchProgress(\n run.id,\n {\n createdCount: 0,\n updatedCount: delta.updatedCount,\n skippedCount: delta.skippedCount,\n failedCount: delta.failedCount,\n batchesCompleted: 1,\n },\n batch.cursor,\n scope,\n { expectedBatchesCompleted: committedBatches, persistSharedCursor },\n )\n committedBatches += 1\n await updateProgress(run.progressJobId, processedCount, null, scope)\n await logExportItemFailures(run.id, run.integrationId, batch.results, scope)\n\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: `Processed export batch ${batch.batchIndex}`,\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Processed ${processedCount} export items so far.`,\n processedCount,\n batchSize: batch.results.length,\n cursor: batch.cursor,\n },\n })\n\n return 'continue'\n },\n )\n if (streamResult === 'stopped') return\n } catch (error) {\n if (error instanceof SyncRunOwnershipConflictError) {\n logger.warn('Yielding export run to a concurrent worker that already advanced it', {\n runId: run.id,\n expectedBatchesCompleted: error.expectedBatchesCompleted,\n })\n return\n }\n const message = error instanceof Error ? error.message : 'Sync export failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)\n },\n }\n}\n\nexport type SyncEngine = ReturnType<typeof createSyncEngine>\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport { STALE_JOB_TIMEOUT_SECONDS } from '../../progress/lib/progressService'\nimport { refreshCoverageSnapshot } from '../../query_index/lib/coverage'\nimport { emitDataSyncEvent } from '../events'\nimport type { DataSyncAdapter, DataMapping, ExportBatch, ImportBatch, RunParameterValue } from './adapter'\nimport { getDataSyncAdapter, resolveProviderKey } from './adapter-registry'\nimport type { SyncRunService } from './sync-run-service'\nimport { SyncRunOwnershipConflictError } from './sync-run-service'\nimport { forEachBatch } from './batch-stream'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n captureTelemetryTrace,\n type TelemetrySpanAttributes,\n} from '@open-mercato/shared/lib/telemetry/runtime'\nimport type { SyncRun } from '../data/entities'\n\nconst logger = createLogger('data_sync').child({ component: 'sync-engine' })\n\ntype RunParameters = Record<string, RunParameterValue>\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n userId?: string | null\n}\n\ntype EngineDeps = {\n em: EntityManager\n syncRunService: SyncRunService\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n integrationStateService?: IntegrationStateService\n progressService: ProgressService\n}\n\n/** Repeated on every batch span so a rooted batch trace identifies its run on its own. */\nfunction runSpanAttributes(run: SyncRun, providerKey: string, scope: SyncScope): TelemetrySpanAttributes {\n return {\n 'data_sync.run_id': run.id,\n 'data_sync.integration_id': run.integrationId,\n 'data_sync.provider_key': providerKey,\n 'data_sync.entity_type': run.entityType,\n 'data_sync.direction': run.direction,\n 'om.tenant_id': scope.tenantId,\n 'om.organization_id': scope.organizationId,\n }\n}\n\nfunction applyImportCounters(batch: ImportBatch): Pick<Required<SyncCounterDelta>, 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount'> {\n let createdCount = 0\n let updatedCount = 0\n let skippedCount = 0\n let failedCount = 0\n\n for (const item of batch.items) {\n if (item.action === 'create') createdCount += 1\n else if (item.action === 'update') updatedCount += 1\n else if (item.action === 'failed') failedCount += 1\n else skippedCount += 1\n }\n\n return { createdCount, updatedCount, skippedCount, failedCount }\n}\n\ntype SyncCounterDelta = {\n createdCount?: number\n updatedCount?: number\n skippedCount?: number\n failedCount?: number\n processedCount: number\n}\n\nfunction applyExportCounters(batch: ExportBatch): SyncCounterDelta {\n let failedCount = 0\n let skippedCount = 0\n let updatedCount = 0\n\n for (const result of batch.results) {\n if (result.status === 'error') failedCount += 1\n else if (result.status === 'skipped') skippedCount += 1\n else updatedCount += 1\n }\n\n return {\n failedCount,\n skippedCount,\n updatedCount,\n processedCount: batch.results.length,\n }\n}\n\n// Adapter batches can legitimately outlast the stale-job sweep window (slow upstream\n// APIs), so the engine must heartbeat while a batch is still being produced. The same\n// tick also polls cancellation, so a cancel lands within one interval instead of waiting\n// out the batch \u2014 sharing this timer rather than adding a second one that would double\n// the per-interval round-trips for the whole life of a run.\nconst HEARTBEAT_TICK_MS = (STALE_JOB_TIMEOUT_SECONDS * 1000) / 4\n\n// Runs `tick` on an interval only while the source iterator is pending, so heartbeats\n// stop the moment the producer dies and genuinely stale jobs still get swept. The outer\n// finally closes the adapter generator on early exits (cancellation, ownership conflict).\n// Our own abort, as opposed to a failure that merely coincided with one. Adapters are told to\n// return rather than throw, but `signal.throwIfAborted()` and an aborted `fetch` both surface as\n// this, and either is a cancellation rather than a fault.\n//\n// Matched structurally on `name` rather than with `instanceof Error`, because those two throw a\n// `DOMException`, and whether that inherits from `Error` depends on the runtime \u2014 it does under\n// bare Node 24 and does NOT under the jest environment this is tested in. An `instanceof` test\n// therefore passes or fails on where the code runs, which is not something cancellation should\n// depend on.\nfunction isAbortError(error: unknown): boolean {\n return typeof error === 'object' && error !== null && (error as { name?: unknown }).name === 'AbortError'\n}\n\nasync function* withHeartbeat<T>(source: AsyncIterable<T>, tick: () => void, intervalMs: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n while (true) {\n const timer = setInterval(tick, intervalMs)\n let result: IteratorResult<T>\n try {\n result = await iterator.next()\n } finally {\n clearInterval(timer)\n }\n if (result.done) return\n yield result.value\n }\n } finally {\n await iterator.return?.()\n }\n}\n\nexport function createSyncEngine(deps: EngineDeps) {\n const { syncRunService, integrationCredentialsService, integrationLogService, integrationStateService, progressService } = deps\n\n async function resolveMapping(adapter: DataSyncAdapter, entityType: string, scope: SyncScope): Promise<DataMapping> {\n return adapter.getMapping({\n entityType,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n })\n }\n\n async function updateProgress(progressJobId: string | null | undefined, processedCount: number, totalCount: number | null, scope: SyncScope): Promise<void> {\n if (!progressJobId) return\n\n await progressService.updateProgress(\n progressJobId,\n {\n processedCount,\n totalCount: totalCount ?? undefined,\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n\n // On redelivery the progress counter must resume where the last delivery left off the\n // same way committedBatches does \u2014 updateProgress writes absolute counts, so starting at\n // zero would regress the visible count. The progress job's own processedCount is the only\n // persisted value already in the engine's unit: `batch.processedCount ?? items.length`,\n // i.e. source records. The run's created/updated/skipped/failed counters count emitted\n // items, which adapters may explode several-per-source-record (Akeneo yields a product\n // plus its variants), so seeding from them would overshoot the total and pin the bar.\n async function seedProcessedCount(progressJobId: string | null | undefined, scope: SyncScope): Promise<number> {\n if (!progressJobId) return 0\n const job = await progressService.getJob(progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n return job?.processedCount ?? 0\n }\n\n function makeHeartbeatTick(progressJobId: string | null | undefined, scope: SyncScope): () => void {\n const touchJobHeartbeat = progressService.touchJobHeartbeat?.bind(progressService)\n if (!progressJobId || !touchJobHeartbeat) return () => {}\n let inFlight = false\n return () => {\n if (inFlight) return\n inFlight = true\n touchJobHeartbeat(progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n .catch((error) => {\n logger.warn('Progress heartbeat failed', {\n progressJobId,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n .finally(() => {\n inFlight = false\n })\n }\n }\n\n // Rides the heartbeat timer, which is the only thing that runs while the adapter is still\n // producing a batch \u2014 the engine's own cancellation check sits in the batch handler and is\n // reached only after a yield. Swallows its own errors because it runs on a timer, where an\n // unhandled rejection is fatal, and stops polling once it has aborted.\n function makeCancellationTick(progressJobId: string | null | undefined, scope: SyncScope, controller: AbortController): () => void {\n if (!progressJobId) return () => {}\n let inFlight = false\n return () => {\n if (inFlight || controller.signal.aborted) return\n inFlight = true\n progressService.isCancellationRequested(progressJobId, scope.tenantId, scope.organizationId)\n .then((cancelled) => {\n if (cancelled) controller.abort()\n })\n .catch((error) => {\n logger.warn('Cancellation poll failed', {\n progressJobId,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n .finally(() => {\n inFlight = false\n })\n }\n }\n\n async function refreshCoverageSnapshots(entityTypes: string[] | undefined, scope: SyncScope): Promise<void> {\n if (!entityTypes || entityTypes.length === 0) return\n\n await Promise.allSettled(\n Array.from(new Set(entityTypes.filter((value) => typeof value === 'string' && value.trim().length > 0)))\n .map((entityType) => refreshCoverageSnapshot(deps.em, {\n entityType,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })),\n )\n }\n\n async function logImportItemFailures(\n runId: string,\n integrationId: string,\n items: ImportBatch['items'],\n scope: SyncScope,\n ): Promise<void> {\n const failedItems = items.filter((item) => item.action === 'failed')\n for (const item of failedItems) {\n const errorMessage = typeof item.data.errorMessage === 'string' && item.data.errorMessage.trim().length > 0\n ? item.data.errorMessage.trim()\n : 'Import item failed'\n const sourceProductUuid = typeof item.data.sourceProductUuid === 'string' && item.data.sourceProductUuid.trim().length > 0\n ? item.data.sourceProductUuid.trim()\n : null\n const sourceIdentifier = typeof item.data.sourceIdentifier === 'string' && item.data.sourceIdentifier.trim().length > 0\n ? item.data.sourceIdentifier.trim()\n : null\n const message = [\n `Failed to import item ${item.externalId}`,\n sourceProductUuid ? `(uuid: ${sourceProductUuid})` : null,\n sourceIdentifier ? `(identifier: ${sourceIdentifier})` : null,\n `: ${errorMessage}`,\n ].filter((part) => part !== null).join(' ')\n\n await integrationLogService.write(\n {\n integrationId,\n runId,\n level: 'error',\n message,\n payload: item.data,\n },\n scope,\n )\n }\n }\n\n async function logExportItemFailures(\n runId: string,\n integrationId: string,\n results: ExportBatch['results'],\n scope: SyncScope,\n ): Promise<void> {\n const failedResults = results.filter((result) => result.status === 'error' && result.error)\n for (const result of failedResults) {\n const label = result.externalId ? `${result.externalId} (id: ${result.localId})` : result.localId\n const errorMessage = result.error!.split('\\n')[0]\n const message = `Failed to export item ${label}: ${errorMessage}`\n\n await integrationLogService.write(\n {\n integrationId,\n runId,\n level: 'error',\n message,\n payload: { kind: 'export-item-failure', summary: result.error },\n },\n scope,\n )\n }\n }\n\n async function writeOperationalLog(params: {\n integrationId: string\n runId: string\n level: 'info' | 'warn' | 'error'\n message: string\n scope: SyncScope\n enabled: boolean\n payload?: Record<string, unknown>\n }): Promise<void> {\n if (!params.enabled) return\n\n await integrationLogService.write(\n {\n integrationId: params.integrationId,\n runId: params.runId,\n level: params.level,\n message: params.message,\n payload: params.payload,\n },\n params.scope,\n )\n }\n\n async function updateOperationalState(params: {\n integrationId: string\n status: 'healthy' | 'degraded' | 'unhealthy'\n scope: SyncScope\n enabled: boolean\n }): Promise<void> {\n if (!params.enabled || !integrationStateService) return\n\n await integrationStateService.upsert(\n params.integrationId,\n {\n lastHealthStatus: params.status,\n lastHealthCheckedAt: new Date(),\n },\n params.scope,\n )\n }\n\n async function finalizeRun(\n runId: string,\n status: 'completed' | 'failed' | 'cancelled',\n scope: SyncScope,\n error?: string,\n operationalTelemetry = false,\n ): Promise<void> {\n const existingRun = await syncRunService.getRun(runId, scope)\n const alreadyFinalizedWithSameStatus = existingRun?.status === status\n && (status === 'completed' || status === 'failed' || status === 'cancelled')\n\n const run = await syncRunService.markStatus(runId, status, scope, error)\n if (!run) return\n\n if (alreadyFinalizedWithSameStatus) {\n return\n }\n\n if (run.status !== status) {\n // `markStatus` refuses a terminal -> different-terminal transition and\n // returns the row unchanged, so the run is already finished under another\n // delivery of this job. Everything below \u2014 the progress job, the\n // operational log and the lifecycle event \u2014 would describe the wrong\n // outcome, and `data_sync.run.failed` is dispatched to tenant webhooks.\n // A displaced worker stays silent instead.\n logger.warn('Skipping finalization of a sync run another worker already finalized', {\n runId,\n requestedStatus: status,\n actualStatus: run.status,\n })\n return\n }\n\n if (run.progressJobId) {\n if (status === 'completed') {\n await progressService.completeJob(\n run.progressJobId,\n {\n resultSummary: {\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'failed') {\n await progressService.failJob(\n run.progressJobId,\n {\n errorMessage: error ?? 'Sync run failed',\n },\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n } else if (status === 'cancelled') {\n await progressService.markCancelled(\n run.progressJobId,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n )\n }\n }\n\n if (status === 'completed') {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'healthy',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run completed',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'completed',\n summary: `Sync completed with ${run.createdCount} created, ${run.updatedCount} updated, ${run.failedCount} failed.`,\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n },\n })\n } else if (status === 'cancelled') {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'warn',\n message: 'Sync run cancelled',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'cancelled',\n summary: 'The sync run was cancelled before completion.',\n },\n })\n } else {\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'unhealthy',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message: error ?? 'Sync run failed',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'failed',\n summary: error ?? 'The sync run failed.',\n },\n })\n }\n\n if (status === 'completed') {\n await emitDataSyncEvent('data_sync.run.completed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n if (status === 'cancelled') {\n await emitDataSyncEvent('data_sync.run.cancelled', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return\n }\n\n await emitDataSyncEvent('data_sync.run.failed', {\n runId,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n error: error ?? null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n\n return {\n async runImport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n logger.warn('Skipping stale import job for missing run', { runId })\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamImport) {\n throw new Error(`No import adapter registered for provider ${providerKey}`)\n }\n const operationalTelemetry = adapter.operationalTelemetry === true\n const persistSharedCursor = adapter.persistsSharedCursor?.(run.entityType) ?? true\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n // A run already `running` means a stalled job was redelivered, so this is\n // a resume rather than a first start. Consumers see one `started` event per\n // delivery either way; the flag is what lets them tell the two apart.\n const resumed = run.status === 'running'\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n resumed,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run started',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Import run started for ${run.entityType}.`,\n entityType: run.entityType,\n direction: run.direction,\n },\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = await seedProcessedCount(run.progressJobId, scope)\n let totalCount: number | null = null\n let committedBatches = activeRun.batchesCompleted ?? 0\n // Whether the last committed batch said the source was exhausted. Distinguishes a stream that\n // drained from one the adapter stopped early \u2014 see the post-stream finalize below.\n let streamReportedDone = false\n // Captured while the triggering job's span is still the active one, so\n // every rooted batch trace can link back to it.\n const runTrace = captureTelemetryTrace()\n const spanAttributes = runSpanAttributes(run, providerKey, scope)\n // Declared outside the try because both the catch and the completion path below read it.\n const cancellation = new AbortController()\n const heartbeat = makeHeartbeatTick(run.progressJobId, scope)\n const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation)\n\n try {\n const streamResult = await forEachBatch(\n withHeartbeat(\n adapter.streamImport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n runId: run.id,\n parameters: (run.parameters ?? {}) as RunParameters,\n signal: cancellation.signal,\n }),\n // `finally` so a synchronous throw from the heartbeat cannot also stop cancellation\n // from being observed for the rest of the run.\n () => { try { heartbeat() } finally { pollCancellation() } },\n HEARTBEAT_TICK_MS,\n ),\n {\n spanName: 'data_sync.import.batch',\n drainSpanName: 'data_sync.import.drain',\n attributes: spanAttributes,\n linkTo: runTrace,\n },\n async (batch, span) => {\n span.setAttributes({\n 'data_sync.batch_index': batch.batchIndex,\n 'data_sync.batch_size': batch.items.length,\n })\n\n if (cancellation.signal.aborted || (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId))) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return 'stop'\n }\n\n const delta = applyImportCounters(batch)\n const processedBatchCount = batch.processedCount ?? batch.items.length\n processedCount += processedBatchCount\n totalCount = batch.totalEstimate ?? totalCount\n\n span.setAttributes({\n 'data_sync.processed_count': processedBatchCount,\n 'data_sync.created_count': delta.createdCount,\n 'data_sync.updated_count': delta.updatedCount,\n 'data_sync.skipped_count': delta.skippedCount,\n 'data_sync.failed_count': delta.failedCount,\n })\n\n await syncRunService.commitBatchProgress(\n run.id,\n {\n ...delta,\n batchesCompleted: 1,\n },\n batch.cursor,\n scope,\n { expectedBatchesCompleted: committedBatches, persistSharedCursor },\n )\n committedBatches += 1\n streamReportedDone = batch.hasMore === false\n\n await updateProgress(run.progressJobId, processedCount, totalCount, scope)\n await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope)\n await logImportItemFailures(run.id, run.integrationId, batch.items, scope)\n\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: batch.message?.trim().length\n ? batch.message.trim()\n : `Processed import batch ${batch.batchIndex}`,\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Processed ${processedCount}${totalCount ? ` of ${totalCount}` : ''} rows so far.`,\n processedCount,\n batchSize: batch.items.length,\n processedBatchCount,\n cursor: batch.cursor,\n },\n })\n\n return 'continue'\n },\n )\n if (streamResult === 'stopped') return\n } catch (error) {\n if (error instanceof SyncRunOwnershipConflictError) {\n logger.warn('Yielding import run to a concurrent worker that already advanced it', {\n runId: run.id,\n expectedBatchesCompleted: error.expectedBatchesCompleted,\n })\n return\n }\n // An adapter that honours the signal may reject instead of returning, so our own abort is\n // a cancellation rather than a fault \u2014 and must not leave an `error` entry in the\n // integration log for a run the operator cancelled on purpose. Anything else that merely\n // coincided with the cancel \u2014 a rejecting commit, an upstream 500 \u2014 is a genuine failure\n // and keeps its log entry, its message, its `failed` status and its failed event.\n if (cancellation.signal.aborted && isAbortError(error)) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return\n }\n const message = error instanceof Error ? error.message : 'Sync import failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)\n return\n }\n\n // An adapter that honours the signal stops mid-batch and returns WITHOUT yielding, so the\n // batch handler \u2014 which owns the only other `cancelled` transition \u2014 never runs and the\n // stream reports `completed`.\n //\n // `streamReportedDone` keeps that from swallowing a run that genuinely finished: an adapter\n // that ignores the signal and drains after reporting `hasMore: false` delivered everything it\n // had, even when the cancel landed during the final read. Calling that cancelled would tell\n // the operator a complete sync was partial and leave a finished run resumable.\n if (cancellation.signal.aborted && !streamReportedDone) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)\n },\n\n async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {\n const run = await syncRunService.getRun(runId, scope)\n if (!run) {\n logger.warn('Skipping stale export job for missing run', { runId })\n return\n }\n if (run.status === 'cancelled') {\n if (run.progressJobId) {\n await progressService.markCancelled(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n return\n }\n\n const providerKey = resolveProviderKey(run.integrationId)\n const adapter = getDataSyncAdapter(providerKey)\n if (!adapter?.streamExport) {\n throw new Error(`No export adapter registered for provider ${providerKey}`)\n }\n const operationalTelemetry = adapter.operationalTelemetry === true\n const persistSharedCursor = adapter.persistsSharedCursor?.(run.entityType) ?? true\n\n const credentials = await integrationCredentialsService.resolve(run.integrationId, scope)\n if (!credentials) {\n throw new Error(`Integration ${run.integrationId} is missing credentials`)\n }\n\n // A run already `running` means a stalled job was redelivered, so this is\n // a resume rather than a first start. Consumers see one `started` event per\n // delivery either way; the flag is what lets them tell the two apart.\n const resumed = run.status === 'running'\n const activeRun = await syncRunService.markStatus(run.id, 'running', scope)\n if (!activeRun || activeRun.status !== 'running') {\n return\n }\n await emitDataSyncEvent('data_sync.run.started', {\n runId: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n resumed,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n await updateOperationalState({\n integrationId: run.integrationId,\n status: 'degraded',\n scope,\n enabled: operationalTelemetry,\n })\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: 'Sync run started',\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Export run started for ${run.entityType}.`,\n entityType: run.entityType,\n direction: run.direction,\n },\n })\n\n if (run.progressJobId) {\n await progressService.startJob(run.progressJobId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n })\n }\n\n const mapping = await resolveMapping(adapter, run.entityType, scope)\n let processedCount = await seedProcessedCount(run.progressJobId, scope)\n let committedBatches = activeRun.batchesCompleted ?? 0\n // Whether the last committed batch said the source was exhausted. Distinguishes a stream that\n // drained from one the adapter stopped early \u2014 see the post-stream finalize below.\n let streamReportedDone = false\n // Captured while the triggering job's span is still the active one, so\n // every rooted batch trace can link back to it.\n const runTrace = captureTelemetryTrace()\n const spanAttributes = runSpanAttributes(run, providerKey, scope)\n // Declared outside the try because both the catch and the completion path below read it.\n const cancellation = new AbortController()\n const heartbeat = makeHeartbeatTick(run.progressJobId, scope)\n const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation)\n\n try {\n const streamResult = await forEachBatch(\n withHeartbeat(\n adapter.streamExport({\n entityType: run.entityType,\n cursor: run.cursor ?? undefined,\n batchSize,\n credentials,\n mapping,\n scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },\n runId: run.id,\n parameters: (run.parameters ?? {}) as RunParameters,\n signal: cancellation.signal,\n }),\n // `finally` so a synchronous throw from the heartbeat cannot also stop cancellation\n // from being observed for the rest of the run.\n () => { try { heartbeat() } finally { pollCancellation() } },\n HEARTBEAT_TICK_MS,\n ),\n {\n spanName: 'data_sync.export.batch',\n drainSpanName: 'data_sync.export.drain',\n attributes: spanAttributes,\n linkTo: runTrace,\n },\n async (batch, span) => {\n span.setAttributes({\n 'data_sync.batch_index': batch.batchIndex,\n 'data_sync.batch_size': batch.results.length,\n })\n\n if (cancellation.signal.aborted || (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId))) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return 'stop'\n }\n\n const delta = applyExportCounters(batch)\n processedCount += delta.processedCount\n\n span.setAttributes({\n 'data_sync.processed_count': delta.processedCount,\n 'data_sync.updated_count': delta.updatedCount,\n 'data_sync.skipped_count': delta.skippedCount,\n 'data_sync.failed_count': delta.failedCount,\n })\n\n await syncRunService.commitBatchProgress(\n run.id,\n {\n createdCount: 0,\n updatedCount: delta.updatedCount,\n skippedCount: delta.skippedCount,\n failedCount: delta.failedCount,\n batchesCompleted: 1,\n },\n batch.cursor,\n scope,\n { expectedBatchesCompleted: committedBatches, persistSharedCursor },\n )\n committedBatches += 1\n streamReportedDone = batch.hasMore === false\n await updateProgress(run.progressJobId, processedCount, null, scope)\n await logExportItemFailures(run.id, run.integrationId, batch.results, scope)\n\n await writeOperationalLog({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'info',\n message: `Processed export batch ${batch.batchIndex}`,\n scope,\n enabled: operationalTelemetry,\n payload: {\n operationalStatus: 'running',\n summary: `Processed ${processedCount} export items so far.`,\n processedCount,\n batchSize: batch.results.length,\n cursor: batch.cursor,\n },\n })\n\n return 'continue'\n },\n )\n if (streamResult === 'stopped') return\n } catch (error) {\n if (error instanceof SyncRunOwnershipConflictError) {\n logger.warn('Yielding export run to a concurrent worker that already advanced it', {\n runId: run.id,\n expectedBatchesCompleted: error.expectedBatchesCompleted,\n })\n return\n }\n // An adapter that honours the signal may reject instead of returning, so our own abort is\n // a cancellation rather than a fault \u2014 and must not leave an `error` entry in the\n // integration log for a run the operator cancelled on purpose. Anything else that merely\n // coincided with the cancel \u2014 a rejecting commit, an upstream 500 \u2014 is a genuine failure\n // and keeps its log entry, its message, its `failed` status and its failed event.\n if (cancellation.signal.aborted && isAbortError(error)) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return\n }\n const message = error instanceof Error ? error.message : 'Sync export failed'\n await integrationLogService.write(\n {\n integrationId: run.integrationId,\n runId: run.id,\n level: 'error',\n message,\n },\n scope,\n )\n await finalizeRun(run.id, 'failed', scope, message, operationalTelemetry)\n return\n }\n\n // An adapter that honours the signal stops mid-batch and returns WITHOUT yielding, so the\n // batch handler \u2014 which owns the only other `cancelled` transition \u2014 never runs and the\n // stream reports `completed`.\n //\n // `streamReportedDone` keeps that from swallowing a run that genuinely finished: an adapter\n // that ignores the signal and drains after reporting `hasMore: false` delivered everything it\n // had, even when the cancel landed during the final read. Calling that cancelled would tell\n // the operator a complete sync was partial and leave a finished run resumable.\n if (cancellation.signal.aborted && !streamReportedDone) {\n await finalizeRun(run.id, 'cancelled', scope, undefined, operationalTelemetry)\n return\n }\n\n await finalizeRun(run.id, 'completed', scope, undefined, operationalTelemetry)\n },\n }\n}\n\nexport type SyncEngine = ReturnType<typeof createSyncEngine>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -809,6 +809,7 @@ const syncExcelCustomersAdapter = {
|
|
|
809
809
|
const batchRows = batch.rows;
|
|
810
810
|
const items = [];
|
|
811
811
|
for (let index = 0; index < batchRows.length; index += 1) {
|
|
812
|
+
if (input.signal?.aborted) return;
|
|
812
813
|
items.push(await processRow({
|
|
813
814
|
row: batchRows[index],
|
|
814
815
|
rowNumber: batch.rowStart + index + 1,
|