@memberjunction/integration-progress-artifacts 0.0.1 → 5.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ import type { IntegrationProgressEvent, IntegrationProgressEventType, IntegrationRunManifest } from './types.js';
2
+ export interface EmitterOptions {
3
+ /** Root directory containing per-run subdirs. Defaults to ./logs/integration-runs. */
4
+ rootDir?: string;
5
+ /** Optional human-facing console mirror (default false — file is the primary record). */
6
+ consoleMirror?: boolean;
7
+ }
8
+ /**
9
+ * Side-channel hook invoked for EVERY emitted event. The server layer registers this once at
10
+ * startup to fan events onto a live GraphQL subscription topic (plan.md §11) WITHOUT this package
11
+ * depending on the server (inversion of control). The durable JSONL artifact + the pollable
12
+ * tail query remain the source of truth; this is the additive push path. Best-effort — a throwing
13
+ * hook never breaks the sync or the durable write.
14
+ */
15
+ export type IntegrationProgressPublishHook = (manifest: IntegrationRunManifest, event: IntegrationProgressEvent) => void;
16
+ /**
17
+ * Writes structured progress artifacts to `<rootDir>/<runID>/`:
18
+ * - manifest.json (written once at start)
19
+ * - progress.jsonl (append-only event stream; checkpoint events carry resumableState)
20
+ * - result.json (written once at terminate)
21
+ *
22
+ * Sequence numbers monotonic per emitter instance. Disk I/O is async and serialized
23
+ * via an internal promise chain — callers don't await; the emitter guarantees ordered
24
+ * writes. A flush() method awaits the queue when needed (test teardown, run completion).
25
+ */
26
+ export declare class IntegrationProgressEmitter {
27
+ private readonly manifest;
28
+ private readonly runDir;
29
+ private readonly progressPath;
30
+ private readonly manifestPath;
31
+ private readonly resultPath;
32
+ private readonly consoleMirror;
33
+ private writeChain;
34
+ private seq;
35
+ private terminated;
36
+ private aggregateCounts;
37
+ private errors;
38
+ private warnings;
39
+ private latestCheckpointSeq?;
40
+ private readonly startMs;
41
+ private static _publishHook?;
42
+ /**
43
+ * Register (or clear, with no arg) the global publish hook. The server registers this at startup
44
+ * so every emitted event also fans out to the live GraphQL subscription, in addition to the
45
+ * durable JSONL artifact + the pollable tail query.
46
+ */
47
+ static SetPublishHook(hook?: IntegrationProgressPublishHook): void;
48
+ constructor(manifest: IntegrationRunManifest, opts?: EmitterOptions);
49
+ /** Emit a generic event. */
50
+ emit(eventType: IntegrationProgressEventType, partial?: Partial<Omit<IntegrationProgressEvent, 'ts' | 'seq' | 'eventType'>>): void;
51
+ /** Convenience helpers — sugared `emit()` for the common cases. */
52
+ runStart(message?: string): void;
53
+ stageStart(stage: string, message?: string): void;
54
+ stageComplete(stage: string, counts?: IntegrationProgressEvent['counts']): void;
55
+ stageError(stage: string, message: string, data?: Record<string, unknown>): void;
56
+ /**
57
+ * Emit a non-fatal warning. Carries a structured {stage, code, message, data}
58
+ * payload that the reader aggregates into the run result's `warnings[]` rollup.
59
+ * Unlike `stageError`, a warning never fails the run.
60
+ */
61
+ warning(stage: string, code: string, message: string, data?: Record<string, unknown>): void;
62
+ heartbeat(stage: string, message: string, counts?: IntegrationProgressEvent['counts']): void;
63
+ /**
64
+ * Write a resumable checkpoint. resumableState should carry enough subsystem-
65
+ * specific data for the originating service to resume from this point.
66
+ */
67
+ checkpoint(stage: string, resumableState: Record<string, unknown>): void;
68
+ externalCallStart(url: string, method: string, data?: Record<string, unknown>): void;
69
+ externalCallComplete(url: string, method: string, status: number, durationMs: number): void;
70
+ objectAdded(objectName: string, source: 'Declared' | 'Discovered' | 'Custom'): void;
71
+ fieldAdded(objectName: string, fieldName: string, source: 'Declared' | 'Discovered' | 'Custom'): void;
72
+ pkClassifierInvoked(objectName: string): void;
73
+ pkClassifierResult(objectName: string, verdict: Record<string, unknown>): void;
74
+ entityGenerated(objectName: string, mjEntityName: string): void;
75
+ entitySkippedNoPK(objectName: string): void;
76
+ /** Terminate the run as success. */
77
+ complete(message?: string): Promise<void>;
78
+ /** Terminate the run as failure. */
79
+ fail(message: string, code?: string): Promise<void>;
80
+ /**
81
+ * Terminate the run as CANCELLED (a user/system abort stopped it mid-flight, not a failure and not a
82
+ * clean completion). Emits exitReason='aborted' so a cancelled run is distinguishable from one that
83
+ * finished — the persisted CompanyIntegrationRun has no 'Cancelled' status, so the artifact's
84
+ * ExitReason is the GQL-visible signal that the run was stopped early (partial state is still durable).
85
+ */
86
+ cancel(message?: string): Promise<void>;
87
+ /** Await all pending writes. */
88
+ flush(): Promise<void>;
89
+ private bootstrap;
90
+ private writeTerminal;
91
+ /** Reconstruct a {@link SyncWarning} from an emitted `'warning'` event. */
92
+ private warningFromEvent;
93
+ private mirrorToConsole;
94
+ static newRunID(prefix?: string): string;
95
+ }
96
+ //# sourceMappingURL=IntegrationProgressEmitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationProgressEmitter.d.ts","sourceRoot":"","sources":["../src/IntegrationProgressEmitter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,249 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { CountsContributeToAggregate } from './types.js';
4
+ /**
5
+ * Writes structured progress artifacts to `<rootDir>/<runID>/`:
6
+ * - manifest.json (written once at start)
7
+ * - progress.jsonl (append-only event stream; checkpoint events carry resumableState)
8
+ * - result.json (written once at terminate)
9
+ *
10
+ * Sequence numbers monotonic per emitter instance. Disk I/O is async and serialized
11
+ * via an internal promise chain — callers don't await; the emitter guarantees ordered
12
+ * writes. A flush() method awaits the queue when needed (test teardown, run completion).
13
+ */
14
+ export class IntegrationProgressEmitter {
15
+ /**
16
+ * Register (or clear, with no arg) the global publish hook. The server registers this at startup
17
+ * so every emitted event also fans out to the live GraphQL subscription, in addition to the
18
+ * durable JSONL artifact + the pollable tail query.
19
+ */
20
+ static SetPublishHook(hook) {
21
+ IntegrationProgressEmitter._publishHook = hook;
22
+ }
23
+ constructor(manifest, opts = {}) {
24
+ this.manifest = manifest;
25
+ this.writeChain = Promise.resolve();
26
+ this.seq = 0;
27
+ this.terminated = false;
28
+ this.aggregateCounts = { processed: 0, succeeded: 0, failed: 0, skipped: 0 };
29
+ this.errors = [];
30
+ this.warnings = [];
31
+ this.startMs = Date.now();
32
+ const root = opts.rootDir ?? join(process.cwd(), 'logs', 'integration-runs');
33
+ this.runDir = join(root, this.manifest.runID);
34
+ this.progressPath = join(this.runDir, 'progress.jsonl');
35
+ this.manifestPath = join(this.runDir, 'manifest.json');
36
+ this.resultPath = join(this.runDir, 'result.json');
37
+ this.consoleMirror = opts.consoleMirror ?? false;
38
+ this.writeChain = this.bootstrap();
39
+ }
40
+ /** Emit a generic event. */
41
+ emit(eventType, partial = {}) {
42
+ if (this.terminated)
43
+ return;
44
+ const event = {
45
+ ts: new Date().toISOString(),
46
+ seq: ++this.seq,
47
+ eventType,
48
+ ...partial,
49
+ };
50
+ if (eventType === 'checkpoint')
51
+ this.latestCheckpointSeq = event.seq;
52
+ // Count accumulation — applied vs. fetched distinction (see counts shape in types.ts).
53
+ // `stage.complete` (and the terminal `run.complete`) carry the authoritative APPLIED
54
+ // quartet {processed,succeeded,failed,skipped} for a finished stage/run. By contrast,
55
+ // `records.batch.complete` carries a FETCHED `processed` count for live per-batch
56
+ // progress only — the SAME records are later reported again, applied, in `stage.complete`.
57
+ // Summing both double-counts every record (a 56-record sync would report 112). So only
58
+ // the applied-rollup events feed the run-level aggregate; batch/heartbeat counts stay in
59
+ // the stream for progress bars but are NOT summed here.
60
+ if (partial.counts && CountsContributeToAggregate(eventType)) {
61
+ this.aggregateCounts.processed += partial.counts.processed ?? 0;
62
+ this.aggregateCounts.succeeded += partial.counts.succeeded ?? 0;
63
+ this.aggregateCounts.failed += partial.counts.failed ?? 0;
64
+ this.aggregateCounts.skipped += partial.counts.skipped ?? 0;
65
+ }
66
+ if (eventType === 'stage.error' || eventType === 'record.error' || eventType === 'run.fail') {
67
+ const code = typeof event.data?.code === 'string' ? event.data.code : undefined;
68
+ this.errors.push({
69
+ stage: event.stage,
70
+ message: event.message ?? `${eventType} (no message)`,
71
+ code,
72
+ });
73
+ }
74
+ if (eventType === 'warning') {
75
+ this.warnings.push(this.warningFromEvent(event));
76
+ }
77
+ const line = JSON.stringify(event) + '\n';
78
+ if (this.consoleMirror) {
79
+ this.mirrorToConsole(event);
80
+ }
81
+ // §11 push path: fan out to the live subscription via the registered hook. Best-effort —
82
+ // never breaks the sync or the durable write (which remain the source of truth).
83
+ const hook = IntegrationProgressEmitter._publishHook;
84
+ if (hook) {
85
+ try {
86
+ hook(this.manifest, event);
87
+ }
88
+ catch { /* publish is best-effort */ }
89
+ }
90
+ this.writeChain = this.writeChain.then(() => fs.appendFile(this.progressPath, line, 'utf-8'));
91
+ }
92
+ /** Convenience helpers — sugared `emit()` for the common cases. */
93
+ runStart(message) {
94
+ this.emit('run.start', { message, level: 'info' });
95
+ }
96
+ stageStart(stage, message) {
97
+ this.emit('stage.start', { stage, message, level: 'info' });
98
+ }
99
+ stageComplete(stage, counts) {
100
+ this.emit('stage.complete', { stage, counts, level: 'info' });
101
+ }
102
+ stageError(stage, message, data) {
103
+ this.emit('stage.error', { stage, message, level: 'error', data });
104
+ }
105
+ /**
106
+ * Emit a non-fatal warning. Carries a structured {stage, code, message, data}
107
+ * payload that the reader aggregates into the run result's `warnings[]` rollup.
108
+ * Unlike `stageError`, a warning never fails the run.
109
+ */
110
+ warning(stage, code, message, data) {
111
+ this.emit('warning', {
112
+ stage,
113
+ message,
114
+ level: 'warn',
115
+ data: { code, ...(data ?? {}) },
116
+ });
117
+ }
118
+ heartbeat(stage, message, counts) {
119
+ this.emit('progress.heartbeat', { stage, message, counts, level: 'info' });
120
+ }
121
+ /**
122
+ * Write a resumable checkpoint. resumableState should carry enough subsystem-
123
+ * specific data for the originating service to resume from this point.
124
+ */
125
+ checkpoint(stage, resumableState) {
126
+ this.emit('checkpoint', { stage, resumableState, level: 'debug' });
127
+ }
128
+ externalCallStart(url, method, data) {
129
+ this.emit('external.call.start', { data: { url, method, ...(data ?? {}) }, level: 'debug' });
130
+ }
131
+ externalCallComplete(url, method, status, durationMs) {
132
+ this.emit('external.call.complete', {
133
+ data: { url, method, status, durationMs },
134
+ level: status >= 400 ? 'warn' : 'debug',
135
+ });
136
+ }
137
+ objectAdded(objectName, source) {
138
+ this.emit('discovery.object.added', { data: { objectName, source }, level: 'info' });
139
+ }
140
+ fieldAdded(objectName, fieldName, source) {
141
+ this.emit('discovery.field.added', { data: { objectName, fieldName, source }, level: 'debug' });
142
+ }
143
+ pkClassifierInvoked(objectName) {
144
+ this.emit('pk.classifier.invoked', { data: { objectName }, level: 'info' });
145
+ }
146
+ pkClassifierResult(objectName, verdict) {
147
+ this.emit('pk.classifier.result', { data: { objectName, ...verdict }, level: 'info' });
148
+ }
149
+ entityGenerated(objectName, mjEntityName) {
150
+ this.emit('entity.generated', { data: { objectName, mjEntityName }, level: 'info' });
151
+ }
152
+ entitySkippedNoPK(objectName) {
153
+ this.emit('entity.skipped-no-pk', { data: { objectName }, level: 'warn' });
154
+ }
155
+ /** Terminate the run as success. */
156
+ async complete(message) {
157
+ if (this.terminated)
158
+ return;
159
+ this.emit('run.complete', { message, level: 'info' });
160
+ await this.writeTerminal({
161
+ success: true,
162
+ exitReason: 'completed',
163
+ });
164
+ }
165
+ /** Terminate the run as failure. */
166
+ async fail(message, code) {
167
+ if (this.terminated)
168
+ return;
169
+ this.emit('run.fail', { message, level: 'error', data: code ? { code } : undefined });
170
+ await this.writeTerminal({
171
+ success: false,
172
+ exitReason: code === 'budget-exhausted' ? 'budget-exhausted' : 'failed',
173
+ });
174
+ }
175
+ /**
176
+ * Terminate the run as CANCELLED (a user/system abort stopped it mid-flight, not a failure and not a
177
+ * clean completion). Emits exitReason='aborted' so a cancelled run is distinguishable from one that
178
+ * finished — the persisted CompanyIntegrationRun has no 'Cancelled' status, so the artifact's
179
+ * ExitReason is the GQL-visible signal that the run was stopped early (partial state is still durable).
180
+ */
181
+ async cancel(message) {
182
+ if (this.terminated)
183
+ return;
184
+ this.emit('run.cancel', { message: message ?? 'Sync cancelled by user', level: 'warn' });
185
+ await this.writeTerminal({
186
+ success: false,
187
+ exitReason: 'aborted',
188
+ });
189
+ }
190
+ /** Await all pending writes. */
191
+ async flush() {
192
+ await this.writeChain;
193
+ }
194
+ // ── Internals ──────────────────────────────────────────────────────
195
+ async bootstrap() {
196
+ await fs.mkdir(this.runDir, { recursive: true });
197
+ await fs.writeFile(this.manifestPath, JSON.stringify(this.manifest, null, 2), 'utf-8');
198
+ }
199
+ async writeTerminal(partial) {
200
+ this.terminated = true;
201
+ await this.writeChain;
202
+ const result = {
203
+ runID: this.manifest.runID,
204
+ completedAt: new Date().toISOString(),
205
+ success: partial.success,
206
+ exitReason: partial.exitReason,
207
+ durationMs: Date.now() - this.startMs,
208
+ aggregateCounts: this.aggregateCounts,
209
+ errors: this.errors.length > 0 ? this.errors : undefined,
210
+ warnings: this.warnings.length > 0 ? this.warnings : undefined,
211
+ warningCount: this.warnings.length,
212
+ resumableFromSeq: this.latestCheckpointSeq,
213
+ };
214
+ await fs.writeFile(this.resultPath, JSON.stringify(result, null, 2), 'utf-8');
215
+ }
216
+ /** Reconstruct a {@link SyncWarning} from an emitted `'warning'` event. */
217
+ warningFromEvent(event) {
218
+ const { code, ...rest } = event.data ?? {};
219
+ return {
220
+ code: typeof code === 'string' ? code : 'UNKNOWN',
221
+ stage: event.stage ?? '',
222
+ message: event.message ?? '',
223
+ data: Object.keys(rest).length > 0 ? rest : undefined,
224
+ };
225
+ }
226
+ mirrorToConsole(event) {
227
+ const level = event.level ?? 'info';
228
+ const tag = `[${event.eventType}]${event.stage ? ` [${event.stage}]` : ''}`;
229
+ const msg = event.message ?? '';
230
+ switch (level) {
231
+ case 'error':
232
+ console.error(tag, msg);
233
+ break;
234
+ case 'warn':
235
+ console.warn(tag, msg);
236
+ break;
237
+ case 'debug':
238
+ console.debug(tag, msg);
239
+ break;
240
+ default: console.log(tag, msg);
241
+ }
242
+ }
243
+ static newRunID(prefix) {
244
+ const hi = Math.floor(Math.random() * 0xffffffff).toString(16).padStart(8, '0');
245
+ const lo = Math.floor(Math.random() * 0xffffffff).toString(16).padStart(8, '0');
246
+ return `${prefix ?? 'run'}-${Date.now()}-${hi}${lo}`;
247
+ }
248
+ }
249
+ //# sourceMappingURL=IntegrationProgressEmitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationProgressEmitter.js","sourceRoot":"","sources":["../src/IntegrationProgressEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AA2BzD;;;;;;;;;GASG;AACH,MAAM,OAAO,0BAA0B;IAgBnC;;;;OAIG;IACI,MAAM,CAAC,cAAc,CAAC,IAAqC;QAC9D,0BAA0B,CAAC,YAAY,GAAG,IAAI,CAAC;IACnD,CAAC;IAED,YACqB,QAAgC,EACjD,OAAuB,EAAE;QADR,aAAQ,GAAR,QAAQ,CAAwB;QApB7C,eAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,QAAG,GAAG,CAAC,CAAC;QACR,eAAU,GAAG,KAAK,CAAC;QACnB,oBAAe,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACxE,WAAM,GAAgD,EAAE,CAAC;QACzD,aAAQ,GAAkB,EAAE,CAAC;QAEpB,YAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAgBlC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QACxD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACnD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,KAAK,CAAC;QACjD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IACvC,CAAC;IAED,4BAA4B;IACrB,IAAI,CACP,SAAuC,EACvC,UAA+E,EAAE;QAEjF,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,MAAM,KAAK,GAA6B;YACpC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG;YACf,SAAS;YACT,GAAG,OAAO;SACb,CAAC;QACF,IAAI,SAAS,KAAK,YAAY;YAAE,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAC,GAAG,CAAC;QACrE,uFAAuF;QACvF,qFAAqF;QACrF,sFAAsF;QACtF,kFAAkF;QAClF,2FAA2F;QAC3F,uFAAuF;QACvF,yFAAyF;QACzF,wDAAwD;QACxD,IAAI,OAAO,CAAC,MAAM,IAAI,2BAA2B,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,eAAe,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,eAAe,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,eAAe,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;YAC1D,IAAI,CAAC,eAAe,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,SAAS,KAAK,aAAa,IAAI,SAAS,KAAK,cAAc,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAC1F,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAChF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACb,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,GAAG,SAAS,eAAe;gBACrD,IAAI;aACP,CAAC,CAAC;QACP,CAAC;QACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,yFAAyF;QACzF,iFAAiF;QACjF,MAAM,IAAI,GAAG,0BAA0B,CAAC,YAAY,CAAC;QACrD,IAAI,IAAI,EAAE,CAAC;YAAC,IAAI,CAAC;gBAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;QAAC,CAAC;QACxF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAClG,CAAC;IAED,mEAAmE;IAC5D,QAAQ,CAAC,OAAgB;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC;IACM,UAAU,CAAC,KAAa,EAAE,OAAgB;QAC7C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IACM,aAAa,CAAC,KAAa,EAAE,MAA2C;QAC3E,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IACM,UAAU,CAAC,KAAa,EAAE,OAAe,EAAE,IAA8B;QAC5E,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IACD;;;;OAIG;IACI,OAAO,CAAC,KAAa,EAAE,IAAY,EAAE,OAAe,EAAE,IAA8B;QACvF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACjB,KAAK;YACL,OAAO;YACP,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE;SAClC,CAAC,CAAC;IACP,CAAC;IACM,SAAS,CAAC,KAAa,EAAE,OAAe,EAAE,MAA2C;QACxF,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD;;;OAGG;IACI,UAAU,CAAC,KAAa,EAAE,cAAuC;QACpE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,CAAC;IAEM,iBAAiB,CAAC,GAAW,EAAE,MAAc,EAAE,IAA8B;QAChF,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACjG,CAAC;IACM,oBAAoB,CAAC,GAAW,EAAE,MAAc,EAAE,MAAc,EAAE,UAAkB;QACvF,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE;YAChC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE;YACzC,KAAK,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;SAC1C,CAAC,CAAC;IACP,CAAC;IAEM,WAAW,CAAC,UAAkB,EAAE,MAA4C;QAC/E,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACzF,CAAC;IACM,UAAU,CAAC,UAAkB,EAAE,SAAiB,EAAE,MAA4C;QACjG,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACpG,CAAC;IACM,mBAAmB,CAAC,UAAkB;QACzC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IACM,kBAAkB,CAAC,UAAkB,EAAE,OAAgC;QAC1E,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3F,CAAC;IACM,eAAe,CAAC,UAAkB,EAAE,YAAoB;QAC3D,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACzF,CAAC;IACM,iBAAiB,CAAC,UAAkB;QACvC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,oCAAoC;IAC7B,KAAK,CAAC,QAAQ,CAAC,OAAgB;QAClC,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,aAAa,CAAC;YACrB,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,WAAW;SAC1B,CAAC,CAAC;IACP,CAAC;IAED,oCAAoC;IAC7B,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,IAAa;QAC5C,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QACtF,MAAM,IAAI,CAAC,aAAa,CAAC;YACrB,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ;SAC1E,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,MAAM,CAAC,OAAgB;QAChC,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACzF,MAAM,IAAI,CAAC,aAAa,CAAC;YACrB,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,SAAS;SACxB,CAAC,CAAC;IACP,CAAC;IAED,gCAAgC;IACzB,KAAK,CAAC,KAAK;QACd,MAAM,IAAI,CAAC,UAAU,CAAC;IAC1B,CAAC;IAED,sEAAsE;IAE9D,KAAK,CAAC,SAAS;QACnB,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3F,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAA6E;QACrG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,MAAM,IAAI,CAAC,UAAU,CAAC;QACtB,MAAM,MAAM,GAAyB;YACjC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACrC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO;YACrC,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YACxD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;YAC9D,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAClC,gBAAgB,EAAE,IAAI,CAAC,mBAAmB;SAC7C,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAClF,CAAC;IAED,2EAA2E;IACnE,gBAAgB,CAAC,KAA+B;QACpD,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QAC3C,OAAO;YACH,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YACjD,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;YACxB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;YAC5B,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;SACxD,CAAC;IACN,CAAC;IAEO,eAAe,CAAC,KAA+B;QACnD,MAAM,KAAK,GAA6B,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC;QAC9D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC5E,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QAChC,QAAQ,KAAK,EAAE,CAAC;YACZ,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBAAC,MAAM;YAC7C,KAAK,MAAM;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBAAC,MAAM;YAC3C,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBAAC,MAAM;YAC7C,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAC,MAAe;QAClC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChF,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChF,OAAO,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;IACzD,CAAC;CACJ"}
@@ -0,0 +1,33 @@
1
+ import type { IntegrationProgressEvent, IntegrationRunFilter, IntegrationRunSnapshot } from './types.js';
2
+ /**
3
+ * Read API for the progress artifacts. Frontend / health-check / resumption code
4
+ * uses this to inspect ongoing or completed runs without touching the emitter.
5
+ *
6
+ * Implementation is purely file-system reads; no shared state with the emitter
7
+ * (an emitter in another process can write while this reader tails).
8
+ */
9
+ export declare class IntegrationProgressReader {
10
+ private readonly rootDir;
11
+ constructor(rootDir?: string);
12
+ /** List runs, newest-first by mtime. */
13
+ ListRuns(filter?: IntegrationRunFilter, limit?: number): Promise<IntegrationRunSnapshot[]>;
14
+ /** Read snapshot of a single run. Returns undefined if runID not found. */
15
+ GetRun(runID: string): Promise<IntegrationRunSnapshot | undefined>;
16
+ /** Tail events since a given sequence. */
17
+ Tail(runID: string, sinceSeq?: number): Promise<IntegrationProgressEvent[]>;
18
+ /** Find the latest checkpoint for a run (for resumption). */
19
+ LatestCheckpoint(runID: string): Promise<IntegrationProgressEvent | undefined>;
20
+ /** Find all in-flight (non-terminated) runs — for restart-resumption discovery. */
21
+ FindInFlight(): Promise<IntegrationRunSnapshot[]>;
22
+ private safeReadDir;
23
+ private safeReadFile;
24
+ private safeReadJSON;
25
+ private runMtime;
26
+ private readProgressTail;
27
+ private countLines;
28
+ private aggregateCountsFromTail;
29
+ private aggregateWarningsFromTail;
30
+ /** Reconstruct a {@link SyncWarning} from a persisted `'warning'` event. */
31
+ private warningFromEvent;
32
+ }
33
+ //# sourceMappingURL=IntegrationProgressReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationProgressReader.d.ts","sourceRoot":"","sources":["../src/IntegrationProgressReader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,wBAAwB,EACxB,oBAAoB,EAGpB,sBAAsB,EAEzB,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,qBAAa,yBAAyB;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,MAAwD;IAE9F,wCAAwC;IAC3B,QAAQ,CAAC,MAAM,GAAE,oBAAyB,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAkBvG,2EAA2E;IAC9D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAsB/E,0CAA0C;IAC7B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAenF,6DAA6D;IAChD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAQ3F,mFAAmF;IACtE,YAAY,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;YAMhD,WAAW;YAGX,YAAY;YAGZ,YAAY;YAKZ,QAAQ;YAMR,gBAAgB;YAWhB,UAAU;YAKV,uBAAuB;YAuBvB,yBAAyB;IAcvC,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB;CAS3B"}
@@ -0,0 +1,209 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { CountsContributeToAggregate } from './types.js';
4
+ /**
5
+ * Read API for the progress artifacts. Frontend / health-check / resumption code
6
+ * uses this to inspect ongoing or completed runs without touching the emitter.
7
+ *
8
+ * Implementation is purely file-system reads; no shared state with the emitter
9
+ * (an emitter in another process can write while this reader tails).
10
+ */
11
+ export class IntegrationProgressReader {
12
+ constructor(rootDir = join(process.cwd(), 'logs', 'integration-runs')) {
13
+ this.rootDir = rootDir;
14
+ }
15
+ /** List runs, newest-first by mtime. */
16
+ async ListRuns(filter = {}, limit = 50) {
17
+ const entries = await this.safeReadDir(this.rootDir);
18
+ const snapshots = [];
19
+ for (const runID of entries) {
20
+ const snap = await this.GetRun(runID);
21
+ if (!snap)
22
+ continue;
23
+ if (filter.runKind && snap.manifest.runKind !== filter.runKind)
24
+ continue;
25
+ if (filter.integrationID && snap.manifest.integrationID !== filter.integrationID)
26
+ continue;
27
+ if (filter.companyIntegrationID && snap.manifest.companyIntegrationID !== filter.companyIntegrationID)
28
+ continue;
29
+ if (filter.sinceTs && snap.manifest.startedAt < filter.sinceTs)
30
+ continue;
31
+ if (filter.inFlightOnly && !snap.isInFlight)
32
+ continue;
33
+ const mtime = await this.runMtime(runID);
34
+ snapshots.push({ snap, mtimeMs: mtime });
35
+ }
36
+ snapshots.sort((a, b) => b.mtimeMs - a.mtimeMs);
37
+ return snapshots.slice(0, limit).map(s => s.snap);
38
+ }
39
+ /** Read snapshot of a single run. Returns undefined if runID not found. */
40
+ async GetRun(runID) {
41
+ const runDir = join(this.rootDir, runID);
42
+ const manifest = await this.safeReadJSON(join(runDir, 'manifest.json'));
43
+ if (!manifest)
44
+ return undefined;
45
+ const result = await this.safeReadJSON(join(runDir, 'result.json'));
46
+ const events = await this.readProgressTail(join(runDir, 'progress.jsonl'), 1);
47
+ const latestEvent = events[events.length - 1];
48
+ const allCounts = await this.aggregateCountsFromTail(join(runDir, 'progress.jsonl'));
49
+ const warnings = await this.aggregateWarningsFromTail(join(runDir, 'progress.jsonl'));
50
+ const eventCount = await this.countLines(join(runDir, 'progress.jsonl'));
51
+ return {
52
+ manifest,
53
+ latestEvent,
54
+ eventCount,
55
+ result,
56
+ isInFlight: !result,
57
+ counts: allCounts,
58
+ warnings: warnings.length > 0 ? warnings : undefined,
59
+ warningCount: warnings.length,
60
+ };
61
+ }
62
+ /** Tail events since a given sequence. */
63
+ async Tail(runID, sinceSeq = 0) {
64
+ const path = join(this.rootDir, runID, 'progress.jsonl');
65
+ const raw = await this.safeReadFile(path);
66
+ if (!raw)
67
+ return [];
68
+ const out = [];
69
+ for (const line of raw.split('\n')) {
70
+ if (!line.trim())
71
+ continue;
72
+ try {
73
+ const ev = JSON.parse(line);
74
+ if (ev.seq > sinceSeq)
75
+ out.push(ev);
76
+ }
77
+ catch { /* skip malformed line */ }
78
+ }
79
+ return out;
80
+ }
81
+ /** Find the latest checkpoint for a run (for resumption). */
82
+ async LatestCheckpoint(runID) {
83
+ const events = await this.Tail(runID, 0);
84
+ for (let i = events.length - 1; i >= 0; i--) {
85
+ if (events[i].eventType === 'checkpoint')
86
+ return events[i];
87
+ }
88
+ return undefined;
89
+ }
90
+ /** Find all in-flight (non-terminated) runs — for restart-resumption discovery. */
91
+ async FindInFlight() {
92
+ return this.ListRuns({ inFlightOnly: true }, 1000);
93
+ }
94
+ // ── Internals ──────────────────────────────────────────────────────
95
+ async safeReadDir(p) {
96
+ try {
97
+ return await fs.readdir(p);
98
+ }
99
+ catch {
100
+ return [];
101
+ }
102
+ }
103
+ async safeReadFile(p) {
104
+ try {
105
+ return await fs.readFile(p, 'utf-8');
106
+ }
107
+ catch {
108
+ return undefined;
109
+ }
110
+ }
111
+ async safeReadJSON(p) {
112
+ const raw = await this.safeReadFile(p);
113
+ if (!raw)
114
+ return undefined;
115
+ try {
116
+ return JSON.parse(raw);
117
+ }
118
+ catch {
119
+ return undefined;
120
+ }
121
+ }
122
+ async runMtime(runID) {
123
+ try {
124
+ const s = await fs.stat(join(this.rootDir, runID));
125
+ return s.mtimeMs;
126
+ }
127
+ catch {
128
+ return 0;
129
+ }
130
+ }
131
+ async readProgressTail(path, maxLines) {
132
+ const raw = await this.safeReadFile(path);
133
+ if (!raw)
134
+ return [];
135
+ const lines = raw.split('\n').filter(Boolean);
136
+ const tail = lines.slice(Math.max(0, lines.length - maxLines));
137
+ const out = [];
138
+ for (const line of tail) {
139
+ try {
140
+ out.push(JSON.parse(line));
141
+ }
142
+ catch { /* skip */ }
143
+ }
144
+ return out;
145
+ }
146
+ async countLines(path) {
147
+ const raw = await this.safeReadFile(path);
148
+ if (!raw)
149
+ return 0;
150
+ return raw.split('\n').filter(Boolean).length;
151
+ }
152
+ async aggregateCountsFromTail(path) {
153
+ const raw = await this.safeReadFile(path);
154
+ if (!raw)
155
+ return undefined;
156
+ const totals = { processed: 0, succeeded: 0, failed: 0, skipped: 0 };
157
+ for (const line of raw.split('\n')) {
158
+ if (!line.trim())
159
+ continue;
160
+ try {
161
+ const ev = JSON.parse(line);
162
+ if (!ev.counts)
163
+ continue;
164
+ // Mirror the emitter's rule (CountsContributeToAggregate): only the APPLIED
165
+ // rollup events (`stage.complete`/`run.complete`) feed the total. The FETCHED
166
+ // `processed` on `records.batch.complete` is per-batch progress only and would
167
+ // double-count every record if summed here. Keeping the rule in one place
168
+ // guarantees a re-derived count matches the emitter's running aggregate.
169
+ if (!CountsContributeToAggregate(ev.eventType))
170
+ continue;
171
+ totals.processed += ev.counts.processed ?? 0;
172
+ totals.succeeded += ev.counts.succeeded ?? 0;
173
+ totals.failed += ev.counts.failed ?? 0;
174
+ totals.skipped += ev.counts.skipped ?? 0;
175
+ }
176
+ catch { /* skip */ }
177
+ }
178
+ return totals;
179
+ }
180
+ async aggregateWarningsFromTail(path) {
181
+ const raw = await this.safeReadFile(path);
182
+ if (!raw)
183
+ return [];
184
+ const warnings = [];
185
+ for (const line of raw.split('\n')) {
186
+ if (!line.trim())
187
+ continue;
188
+ try {
189
+ const ev = JSON.parse(line);
190
+ if (ev.eventType !== 'warning')
191
+ continue;
192
+ warnings.push(this.warningFromEvent(ev));
193
+ }
194
+ catch { /* skip */ }
195
+ }
196
+ return warnings;
197
+ }
198
+ /** Reconstruct a {@link SyncWarning} from a persisted `'warning'` event. */
199
+ warningFromEvent(event) {
200
+ const { code, ...rest } = event.data ?? {};
201
+ return {
202
+ code: typeof code === 'string' ? code : 'UNKNOWN',
203
+ stage: event.stage ?? '',
204
+ message: event.message ?? '',
205
+ data: Object.keys(rest).length > 0 ? rest : undefined,
206
+ };
207
+ }
208
+ }
209
+ //# sourceMappingURL=IntegrationProgressReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationProgressReader.js","sourceRoot":"","sources":["../src/IntegrationProgressReader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { IntegrationProgressEmitter } from './IntegrationProgressEmitter.js';
3
+ export type { EmitterOptions } from './IntegrationProgressEmitter.js';
4
+ export { IntegrationProgressReader } from './IntegrationProgressReader.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export { IntegrationProgressEmitter } from './IntegrationProgressEmitter.js';
3
+ export { IntegrationProgressReader } from './IntegrationProgressReader.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAE7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Structured progress event types — generic enough to cover RSU runs, sync runs,
3
+ * connector-creation pipelines, table creation. The vocabulary is fixed; specific
4
+ * subsystems carry their own state shape in the `data` field.
5
+ */
6
+ /** What kind of run produced this progress stream. */
7
+ export type IntegrationRunKind = 'SyncRun' | 'ConnectorCreation' | 'RSU' | 'TableCreation' | 'Discovery' | 'Enrichment' | 'Other';
8
+ /** Event types in the progress.jsonl stream. */
9
+ export type IntegrationProgressEventType =
10
+ /** Run started. */
11
+ 'run.start'
12
+ /** Run terminated successfully. */
13
+ | 'run.complete'
14
+ /** Run terminated with failure. */
15
+ | 'run.fail'
16
+ /** Run cancelled mid-flight by a user/system abort (not a failure, not a clean completion). */
17
+ | 'run.cancel'
18
+ /** Run resumed from a prior checkpoint after a kill/restart. */
19
+ | 'run.resumed'
20
+ /** A named stage within the run started. */
21
+ | 'stage.start'
22
+ /** A named stage within the run completed. */
23
+ | 'stage.complete'
24
+ /** A named stage within the run errored (run may continue or fail). */
25
+ | 'stage.error'
26
+ /** A batch of records is about to be processed (sync paths). */
27
+ | 'records.batch.start'
28
+ /** A batch of records has been processed. */
29
+ | 'records.batch.complete'
30
+ /** A single record errored within a batch. */
31
+ | 'record.error'
32
+ /**
33
+ * A non-fatal warning surfaced during the run. Carries a structured
34
+ * {stage, code, message, data} payload (see {@link SyncWarning}). Distinct
35
+ * from `stage.error`/`record.error`: a warning never fails the run, it is
36
+ * aggregated separately into the result's `warnings[]` rollup.
37
+ */
38
+ | 'warning'
39
+ /** Long-running progress with no record-level granularity. */
40
+ | 'progress.heartbeat'
41
+ /**
42
+ * Resumable checkpoint. Carries `resumableState` — the subsystem-specific
43
+ * shape needed for the run to resume from this exact point. On restart,
44
+ * the resumption engine reads the latest checkpoint event for any in-flight
45
+ * run and hands the resumableState back to the originating service.
46
+ */
47
+ | 'checkpoint'
48
+ /** An outbound API call started (vendor-API debugging, esp. GraphQL). */
49
+ | 'external.call.start'
50
+ /** An outbound API call completed. */
51
+ | 'external.call.complete'
52
+ /** An outbound API call retrying after transient failure. */
53
+ | 'external.call.retry'
54
+ /** TransformRecord (or equivalent) reshaped a record. */
55
+ | 'transform.applied'
56
+ /** Discovery added a new IntegrationObject row. */
57
+ | 'discovery.object.added'
58
+ /** Discovery added a new IntegrationObjectField row. */
59
+ | 'discovery.field.added'
60
+ /** PK classifier invoked for an object. */
61
+ | 'pk.classifier.invoked'
62
+ /** PK classifier returned a verdict. */
63
+ | 'pk.classifier.result'
64
+ /** An MJ entity was generated for an IO row that has a PK. */
65
+ | 'entity.generated'
66
+ /** An IO row was skipped from entity generation because it lacks a PK. */
67
+ | 'entity.skipped-no-pk';
68
+ /** Severity levels for progress events. */
69
+ export type IntegrationProgressLevel = 'info' | 'warn' | 'error' | 'debug';
70
+ /**
71
+ * Whether an event's `counts` feed the run-level APPLIED aggregate
72
+ * ({@link IntegrationRunResult.aggregateCounts} / {@link IntegrationRunSnapshot.counts}).
73
+ *
74
+ * Two count vocabularies flow through the stream and must NOT both be summed:
75
+ * - `stage.complete` / `run.complete` carry the authoritative **applied** quartet
76
+ * {processed,succeeded,failed,skipped} for a finished stage/run → these aggregate.
77
+ * - `records.batch.complete` (and `progress.heartbeat`) carry a *fetched* / in-flight
78
+ * `processed` count for live per-batch progress only. The same records are reported
79
+ * again — applied — by the stage rollup, so summing batch counts double-counts every
80
+ * record (a 56-record sync would report processed:112). These do NOT aggregate.
81
+ *
82
+ * The emitter and the reader both call this so a re-derived count always matches the
83
+ * emitter's running aggregate. Centralizing the rule here keeps the two in lock-step.
84
+ */
85
+ export declare function CountsContributeToAggregate(eventType: IntegrationProgressEventType): boolean;
86
+ /**
87
+ * A structured, non-fatal warning surfaced during an integration run. Emitted via
88
+ * `IntegrationProgressEmitter.warning(...)` as a `'warning'` event and aggregated by
89
+ * the reader into the run result's `warnings[]` rollup (with a `warningCount`).
90
+ * Unlike errors, warnings never fail the run.
91
+ */
92
+ export interface SyncWarning {
93
+ /** Stable, machine-matchable warning code (e.g. 'FIELD_TRUNCATED'). */
94
+ code: string;
95
+ /** The stage that produced the warning. */
96
+ stage: string;
97
+ /** Human-readable warning message. */
98
+ message: string;
99
+ /** Optional subsystem-specific structured payload. */
100
+ data?: Record<string, unknown>;
101
+ }
102
+ /** A single event in the progress.jsonl stream (one line, JSON-encoded). */
103
+ export interface IntegrationProgressEvent {
104
+ /** ISO 8601 timestamp. */
105
+ ts: string;
106
+ /** Sequence number within the run (monotonic). */
107
+ seq: number;
108
+ /** Event type. */
109
+ eventType: IntegrationProgressEventType;
110
+ /** Severity (default 'info'). */
111
+ level?: IntegrationProgressLevel;
112
+ /** Stage name (when applicable). */
113
+ stage?: string;
114
+ /** Free-form human message (optional — UI may format from data instead). */
115
+ message?: string;
116
+ /** Counts snapshot — convenient for UI summary without parsing data. */
117
+ counts?: {
118
+ processed?: number;
119
+ succeeded?: number;
120
+ failed?: number;
121
+ skipped?: number;
122
+ totalKnown?: number;
123
+ };
124
+ /**
125
+ * Resumable state — only on `checkpoint` events.
126
+ * Subsystem-specific shape: sync uses {watermark, lastRecordID, batchIndex, pageCursor},
127
+ * pipelines use {currentStep, stageInputs}, etc. The originating service
128
+ * interprets the shape based on `stage`.
129
+ */
130
+ resumableState?: Record<string, unknown>;
131
+ /** Subsystem-specific structured payload. */
132
+ data?: Record<string, unknown>;
133
+ }
134
+ /** manifest.json — written once at run start. */
135
+ export interface IntegrationRunManifest {
136
+ runID: string;
137
+ runKind: IntegrationRunKind;
138
+ integrationID?: string;
139
+ companyIntegrationID?: string;
140
+ objectName?: string;
141
+ triggerType?: 'Manual' | 'Scheduled' | 'Webhook' | 'Pipeline' | 'Restart';
142
+ startedAt: string;
143
+ expectedStages?: string[];
144
+ /** Free-form context (vendor name, RSU mode, etc.). */
145
+ context?: Record<string, unknown>;
146
+ }
147
+ /** result.json — written once at run end (success or failure). */
148
+ export interface IntegrationRunResult {
149
+ runID: string;
150
+ completedAt: string;
151
+ success: boolean;
152
+ exitReason: 'completed' | 'failed' | 'killed' | 'budget-exhausted' | 'aborted';
153
+ durationMs: number;
154
+ aggregateCounts?: {
155
+ processed: number;
156
+ succeeded: number;
157
+ failed: number;
158
+ skipped: number;
159
+ };
160
+ errors?: Array<{
161
+ stage?: string;
162
+ message: string;
163
+ code?: string;
164
+ }>;
165
+ /** Non-fatal warnings surfaced during the run (rollup of `'warning'` events). */
166
+ warnings?: SyncWarning[];
167
+ /** Total number of `'warning'` events emitted (length of `warnings`). */
168
+ warningCount?: number;
169
+ /** When non-empty, this run is resumable: latest checkpoint event sequence. */
170
+ resumableFromSeq?: number;
171
+ }
172
+ /** Snapshot returned by the reader API for a single run. */
173
+ export interface IntegrationRunSnapshot {
174
+ manifest: IntegrationRunManifest;
175
+ latestEvent?: IntegrationProgressEvent;
176
+ eventCount: number;
177
+ result?: IntegrationRunResult;
178
+ isInFlight: boolean;
179
+ /** Latest counts derived from the event stream. */
180
+ counts?: NonNullable<IntegrationProgressEvent['counts']>;
181
+ /** Non-fatal warnings derived from the `'warning'` events in the stream. */
182
+ warnings?: SyncWarning[];
183
+ /** Total number of `'warning'` events in the stream (length of `warnings`). */
184
+ warningCount?: number;
185
+ }
186
+ /** Filter shape for the reader's ListRuns API. */
187
+ export interface IntegrationRunFilter {
188
+ runKind?: IntegrationRunKind;
189
+ integrationID?: string;
190
+ companyIntegrationID?: string;
191
+ sinceTs?: string;
192
+ inFlightOnly?: boolean;
193
+ }
194
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Structured progress event types — generic enough to cover RSU runs, sync runs,
3
+ * connector-creation pipelines, table creation. The vocabulary is fixed; specific
4
+ * subsystems carry their own state shape in the `data` field.
5
+ */
6
+ /**
7
+ * Whether an event's `counts` feed the run-level APPLIED aggregate
8
+ * ({@link IntegrationRunResult.aggregateCounts} / {@link IntegrationRunSnapshot.counts}).
9
+ *
10
+ * Two count vocabularies flow through the stream and must NOT both be summed:
11
+ * - `stage.complete` / `run.complete` carry the authoritative **applied** quartet
12
+ * {processed,succeeded,failed,skipped} for a finished stage/run → these aggregate.
13
+ * - `records.batch.complete` (and `progress.heartbeat`) carry a *fetched* / in-flight
14
+ * `processed` count for live per-batch progress only. The same records are reported
15
+ * again — applied — by the stage rollup, so summing batch counts double-counts every
16
+ * record (a 56-record sync would report processed:112). These do NOT aggregate.
17
+ *
18
+ * The emitter and the reader both call this so a re-derived count always matches the
19
+ * emitter's running aggregate. Centralizing the rule here keeps the two in lock-step.
20
+ */
21
+ export function CountsContributeToAggregate(eventType) {
22
+ return eventType === 'stage.complete' || eventType === 'run.complete';
23
+ }
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4EH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,2BAA2B,CAAC,SAAuC;IAC/E,OAAO,SAAS,KAAK,gBAAgB,IAAI,SAAS,KAAK,cAAc,CAAC;AAC1E,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,32 @@
1
1
  {
2
2
  "name": "@memberjunction/integration-progress-artifacts",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @memberjunction/integration-progress-artifacts",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
3
+ "type": "module",
4
+ "version": "5.39.0",
5
+ "description": "Structured progress artifacts for integration runs: manifest.json + progress.jsonl (append-only with resumable checkpoints) + result.json. Generic across RSU runs, sync runs, creation pipelines, table creation. Replaces ad-hoc console.log with file-system-durable progress that survives transient failures + server restarts.",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "/dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc && tsc-alias -f",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest"
15
+ },
16
+ "dependencies": {
17
+ "@memberjunction/global": "5.39.0"
18
+ },
19
+ "devDependencies": {
20
+ "typescript": "^5.9.3",
21
+ "tsc-alias": "^1.8.16",
22
+ "vitest": "^4.0.18"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/MemberJunction/MJ"
27
+ },
28
+ "license": "ISC",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
10
32
  }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @memberjunction/integration-progress-artifacts
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@memberjunction/integration-progress-artifacts`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**