@chkit/plugin-ingest 0.2.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +43 -0
  2. package/dist/destination.d.ts +40 -0
  3. package/dist/destination.d.ts.map +1 -0
  4. package/dist/destination.js +65 -0
  5. package/dist/destination.js.map +1 -0
  6. package/dist/errors.d.ts +33 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +133 -0
  9. package/dist/errors.js.map +1 -0
  10. package/dist/executor.d.ts +30 -0
  11. package/dist/executor.d.ts.map +1 -0
  12. package/dist/executor.js +552 -0
  13. package/dist/executor.js.map +1 -0
  14. package/dist/incremental.d.ts +45 -0
  15. package/dist/incremental.d.ts.map +1 -0
  16. package/dist/incremental.js +72 -0
  17. package/dist/incremental.js.map +1 -0
  18. package/dist/index.d.ts +12 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +11 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/journal-history.d.ts +15 -0
  23. package/dist/journal-history.d.ts.map +1 -0
  24. package/dist/journal-history.js +366 -0
  25. package/dist/journal-history.js.map +1 -0
  26. package/dist/journal-records.d.ts +28 -0
  27. package/dist/journal-records.d.ts.map +1 -0
  28. package/dist/journal-records.js +76 -0
  29. package/dist/journal-records.js.map +1 -0
  30. package/dist/journal.d.ts +16 -0
  31. package/dist/journal.d.ts.map +1 -0
  32. package/dist/journal.js +72 -0
  33. package/dist/journal.js.map +1 -0
  34. package/dist/loader.d.ts +12 -0
  35. package/dist/loader.d.ts.map +1 -0
  36. package/dist/loader.js +37 -0
  37. package/dist/loader.js.map +1 -0
  38. package/dist/paginate.d.ts +24 -0
  39. package/dist/paginate.d.ts.map +1 -0
  40. package/dist/paginate.js +37 -0
  41. package/dist/paginate.js.map +1 -0
  42. package/dist/plugin.d.ts +155 -0
  43. package/dist/plugin.d.ts.map +1 -0
  44. package/dist/plugin.js +353 -0
  45. package/dist/plugin.js.map +1 -0
  46. package/dist/queue.d.ts +9 -0
  47. package/dist/queue.d.ts.map +1 -0
  48. package/dist/queue.js +47 -0
  49. package/dist/queue.js.map +1 -0
  50. package/dist/recovery.d.ts +41 -0
  51. package/dist/recovery.d.ts.map +1 -0
  52. package/dist/recovery.js +145 -0
  53. package/dist/recovery.js.map +1 -0
  54. package/dist/registry.d.ts +48 -0
  55. package/dist/registry.d.ts.map +1 -0
  56. package/dist/registry.js +109 -0
  57. package/dist/registry.js.map +1 -0
  58. package/dist/retry.d.ts +12 -0
  59. package/dist/retry.d.ts.map +1 -0
  60. package/dist/retry.js +69 -0
  61. package/dist/retry.js.map +1 -0
  62. package/dist/testing.d.ts +18 -0
  63. package/dist/testing.d.ts.map +1 -0
  64. package/dist/testing.js +54 -0
  65. package/dist/testing.js.map +1 -0
  66. package/dist/types.d.ts +229 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +2 -0
  69. package/dist/types.js.map +1 -0
  70. package/package.json +56 -0
package/dist/plugin.js ADDED
@@ -0,0 +1,353 @@
1
+ import process from 'node:process';
2
+ import { createStatelessClickHouseExecutor } from '@chkit/clickhouse';
3
+ import { createPluginRunner, defineFlags, withFactoryDefaults, } from '@chkit/core';
4
+ import { loadDefinitionModules } from '@chkit/core/schema-loader';
5
+ import { z } from 'zod';
6
+ import { BATCH_ID_COLUMN, createClickHouseDestination, INGESTED_AT_COLUMN, RUN_ID_COLUMN } from './destination.js';
7
+ import { IngestConfigError } from './errors.js';
8
+ import { runIngestion } from './executor.js';
9
+ import { createClickHouseJournal, DEFAULT_JOURNAL_TABLE } from './journal.js';
10
+ import { collectPipelines, selectStreams } from './registry.js';
11
+ import { doctorJournal, repairJournal } from './recovery.js';
12
+ const REQUIRED_COLUMNS = [BATCH_ID_COLUMN, RUN_ID_COLUMN, INGESTED_AT_COLUMN];
13
+ const IngestOptionsSchema = z.object({
14
+ journalTable: z.string().min(1).default(DEFAULT_JOURNAL_TABLE),
15
+ /** Host-default execution budget in seconds. */
16
+ maxDurationSeconds: z.number().positive().default(3600),
17
+ prefetchBatches: z.number().int().positive().default(1),
18
+ });
19
+ const SELECTION_FLAGS = defineFlags([
20
+ { name: '--tag', type: 'string[]', description: 'Exact tag every selected stream must carry (repeatable, AND)', placeholder: '<tag>' },
21
+ ]);
22
+ const RUN_FLAGS = defineFlags([
23
+ ...SELECTION_FLAGS,
24
+ { name: '--backfill', type: 'string', description: 'Stable backfill id; uses an isolated checkpoint namespace', placeholder: '<id>' },
25
+ { name: '--from', type: 'string', description: 'Backfill range lower bound (ISO timestamp)', placeholder: '<timestamp>' },
26
+ { name: '--to', type: 'string', description: 'Backfill range upper bound (ISO timestamp)', placeholder: '<timestamp>' },
27
+ { name: '--max-duration', type: 'string', description: 'Execution budget in seconds', placeholder: '<seconds>' },
28
+ ]);
29
+ const RECOVERY_FLAGS = defineFlags([
30
+ ...SELECTION_FLAGS,
31
+ { name: '--backfill', type: 'string', description: 'Inspect an isolated backfill checkpoint namespace', placeholder: '<id>' },
32
+ ]);
33
+ const REPAIR_FLAGS = defineFlags([
34
+ ...RECOVERY_FLAGS,
35
+ { name: '--apply', type: 'string', description: 'Materialize the reviewed fingerprint with writers stopped (select one stream)', placeholder: '<fingerprint>' },
36
+ ]);
37
+ const runCommand = createPluginRunner({ configErrorClass: IngestConfigError });
38
+ export function createIngestPlugin(options = {}) {
39
+ const optionsSchema = withFactoryDefaults(IngestOptionsSchema, options);
40
+ return {
41
+ manifest: { name: 'ingest', apiVersion: 1 },
42
+ optionsSchema,
43
+ commands: [
44
+ {
45
+ name: 'run',
46
+ description: 'Execute the selected ingestion streams from their journaled checkpoints',
47
+ flags: RUN_FLAGS,
48
+ optionsSchema,
49
+ run: runCommand({
50
+ command: 'run',
51
+ label: 'Ingest run',
52
+ fn: async (context) => {
53
+ const selected = await loadSelection(context);
54
+ const backfill = parseBackfill(context.flags);
55
+ const maxDurationSeconds = parseMaxDuration(context.flags['--max-duration']) ?? context.options.maxDurationSeconds;
56
+ const target = openTarget(context);
57
+ const abort = new AbortController();
58
+ const onSignal = () => abort.abort(new DOMException('Interrupted', 'AbortError'));
59
+ process.once('SIGINT', onSignal);
60
+ process.once('SIGTERM', onSignal);
61
+ try {
62
+ const result = await runIngestion({ selected, backfill }, {
63
+ journal: createClickHouseJournal({
64
+ executor: target.executor,
65
+ database: target.database,
66
+ targetId: target.targetId,
67
+ table: context.options.journalTable,
68
+ }),
69
+ destination: createClickHouseDestination(target.executor),
70
+ signal: abort.signal,
71
+ maxDurationMs: maxDurationSeconds * 1000,
72
+ prefetchBatches: context.options.prefetchBatches,
73
+ log: context.jsonMode ? undefined : (message) => context.print(message),
74
+ });
75
+ if (context.jsonMode) {
76
+ context.print({ command: 'run', ...result });
77
+ }
78
+ else {
79
+ const rows = result.streams.reduce((sum, stream) => sum + stream.rows, 0);
80
+ context.print(`Ingest run ${result.runId}: ${result.ok ? 'ok' : 'incomplete'} (${result.streams.length} streams, ${rows} rows)`);
81
+ }
82
+ return result.ok ? 0 : 1;
83
+ }
84
+ finally {
85
+ process.off('SIGINT', onSignal);
86
+ process.off('SIGTERM', onSignal);
87
+ await target.close();
88
+ }
89
+ },
90
+ }),
91
+ },
92
+ {
93
+ name: 'list',
94
+ description: 'List the ingestion streams in the loaded definition graph',
95
+ flags: SELECTION_FLAGS,
96
+ optionsSchema,
97
+ run: runCommand({
98
+ command: 'list',
99
+ label: 'Ingest list',
100
+ fn: async (context) => {
101
+ const selected = await loadSelection(context);
102
+ const streams = selected.map(describeStream);
103
+ if (context.jsonMode) {
104
+ context.print({ ok: true, command: 'list', streams });
105
+ }
106
+ else {
107
+ for (const stream of streams) {
108
+ context.print(`${stream.streamId} -> ${stream.destination} [${stream.tags.join(', ')}]`);
109
+ }
110
+ }
111
+ return 0;
112
+ },
113
+ }),
114
+ },
115
+ {
116
+ name: 'doctor',
117
+ description: 'Validate journal evidence and diagnose uncertain run histories',
118
+ flags: RECOVERY_FLAGS,
119
+ optionsSchema,
120
+ run: runCommand({
121
+ command: 'doctor',
122
+ label: 'Ingest doctor',
123
+ fn: async (context) => {
124
+ const selected = await loadSelection(context);
125
+ const backfill = parseBackfill(context.flags);
126
+ const target = openTarget(context);
127
+ try {
128
+ const streams = await Promise.all(selected.map(async (entry) => {
129
+ const namespaceId = backfill ? `${entry.stream.id}#backfill:${backfill.id}` : entry.stream.id;
130
+ const report = await doctorJournal(namespaceId, {
131
+ executor: target.executor, database: target.database, targetId: target.targetId, table: context.options.journalTable,
132
+ });
133
+ return { ...describeStream(entry), report };
134
+ }));
135
+ const ok = streams.every((stream) => stream.report.healthy);
136
+ if (context.jsonMode) {
137
+ context.print({ ok, command: 'doctor', targetId: target.targetId, streams });
138
+ }
139
+ else {
140
+ for (const { report } of streams) {
141
+ context.print(`${report.namespaceId}: ${report.healthy ? 'healthy' : report.problems.join('; ')} (${report.runs} runs, selected checkpoint v${report.checkpoint.version}, ${report.selectedEvidenceRows} stream evidence rows)`);
142
+ }
143
+ }
144
+ return ok ? 0 : 1;
145
+ }
146
+ finally {
147
+ await target.close();
148
+ }
149
+ },
150
+ }),
151
+ },
152
+ {
153
+ name: 'repair',
154
+ description: 'Review recovery; --apply materializes a verified journal for explicit configuration activation',
155
+ flags: REPAIR_FLAGS,
156
+ optionsSchema,
157
+ run: runCommand({
158
+ command: 'repair',
159
+ label: 'Ingest repair',
160
+ fn: async (context) => {
161
+ const selected = await loadSelection(context);
162
+ const backfill = parseBackfill(context.flags);
163
+ const apply = context.flags['--apply'];
164
+ if (typeof apply === 'string' && selected.length !== 1) {
165
+ throw new IngestConfigError('--apply requires exactly one selected stream; use --tag to narrow the selection.');
166
+ }
167
+ const target = openTarget(context);
168
+ try {
169
+ const streams = [];
170
+ for (const entry of selected) {
171
+ const namespaceId = backfill ? `${entry.stream.id}#backfill:${backfill.id}` : entry.stream.id;
172
+ const result = await repairJournal(namespaceId, typeof apply === 'string' ? apply : undefined, {
173
+ executor: target.executor, database: target.database, targetId: target.targetId, table: context.options.journalTable,
174
+ });
175
+ streams.push({ ...describeStream(entry), ...result });
176
+ }
177
+ if (context.jsonMode) {
178
+ context.print({ ok: true, command: 'repair', targetId: target.targetId, streams });
179
+ }
180
+ else {
181
+ for (const { applied, plan } of streams) {
182
+ if (plan.healthy) {
183
+ context.print(`${plan.namespaceId}: healthy; ${plan.runs} runs, selected checkpoint v${plan.checkpoint.version}.`);
184
+ continue;
185
+ }
186
+ context.print(`${plan.namespaceId}: ${applied ? 'repair materialized; configuration activation required' : 'repair plan'}; retain ${plan.retainedFacts} facts, selected checkpoint v${plan.checkpoint.version}.`);
187
+ for (const problem of plan.problems)
188
+ context.print(problem);
189
+ context.print(`Evidence: ${plan.evidenceRows} rows ${applied ? 'preserved in' : 'will be archived to'} ${applied ? plan.archiveTable : `${plan.archiveTable}_<copy id>`}; original table retained.`);
190
+ context.print(plan.replayWarning);
191
+ context.print(plan.activation);
192
+ if (!applied && !plan.healthy && plan.repairable)
193
+ context.print(`Review this plan, then run ingest repair --tag stream:${plan.namespaceId.split('#backfill:')[0]}${backfill ? ` --backfill ${backfill.id}` : ''} --apply ${plan.fingerprint}`);
194
+ }
195
+ }
196
+ return 0;
197
+ }
198
+ finally {
199
+ await target.close();
200
+ }
201
+ },
202
+ }),
203
+ },
204
+ {
205
+ name: 'status',
206
+ description: 'Show the committed checkpoint of each selected stream',
207
+ flags: SELECTION_FLAGS,
208
+ optionsSchema,
209
+ run: runCommand({
210
+ command: 'status',
211
+ label: 'Ingest status',
212
+ fn: async (context) => {
213
+ const selected = await loadSelection(context);
214
+ const target = openTarget(context);
215
+ try {
216
+ const journal = createClickHouseJournal({
217
+ executor: target.executor,
218
+ database: target.database,
219
+ targetId: target.targetId,
220
+ table: context.options.journalTable,
221
+ });
222
+ await journal.ensure();
223
+ const streams = await Promise.all(selected.map(async (entry) => {
224
+ const checkpoint = await journal.readCheckpoint(entry.stream.id);
225
+ return { ...describeStream(entry), checkpointVersion: checkpoint.version, checkpoint: checkpoint.envelope ?? null };
226
+ }));
227
+ if (context.jsonMode) {
228
+ context.print({ ok: true, command: 'status', targetId: target.targetId, streams });
229
+ }
230
+ else {
231
+ for (const stream of streams) {
232
+ context.print(`${stream.streamId}: v${stream.checkpointVersion} ${stream.checkpoint ? JSON.stringify(stream.checkpoint.state) : '(no checkpoint)'}`);
233
+ }
234
+ }
235
+ return 0;
236
+ }
237
+ finally {
238
+ await target.close();
239
+ }
240
+ },
241
+ }),
242
+ },
243
+ ],
244
+ hooks: {
245
+ async onCheck(context) {
246
+ const findings = checkGraph(await loadGraph(context.config));
247
+ return {
248
+ plugin: 'ingest',
249
+ evaluated: true,
250
+ ok: findings.every((finding) => finding.severity !== 'error'),
251
+ findings,
252
+ };
253
+ },
254
+ },
255
+ };
256
+ }
257
+ export function ingest(options = {}) {
258
+ return { plugin: createIngestPlugin(options), name: 'ingest', enabled: true, options };
259
+ }
260
+ /** Local, zero-network graph checks: they hold in `--offline` mode too. */
261
+ export function checkGraph(pipelines) {
262
+ const findings = [];
263
+ if (pipelines.length === 0) {
264
+ findings.push({ code: 'ingest_no_pipelines', message: 'No ingestion pipeline is exported by the project entry.', severity: 'warn' });
265
+ }
266
+ for (const pipeline of pipelines) {
267
+ for (const stream of pipeline.streams) {
268
+ const columns = new Set(stream.destination.columns.map((column) => column.name));
269
+ const missing = REQUIRED_COLUMNS.filter((name) => !columns.has(name));
270
+ if (missing.length > 0) {
271
+ findings.push({
272
+ code: 'ingest_missing_metadata_columns',
273
+ message: `Stream "${stream.id}" destination ${stream.destination.database}.${stream.destination.name} is missing ${missing.join(', ')}. Spread ingestionColumns into its columns.`,
274
+ severity: 'error',
275
+ });
276
+ }
277
+ }
278
+ }
279
+ return findings;
280
+ }
281
+ // Discover values exported from the configured entry (or legacy schema files).
282
+ async function loadGraph(config) {
283
+ if (config.schema.length === 0)
284
+ return [];
285
+ return collectPipelines(await loadDefinitionModules(config.schema, { cwd: process.cwd() }));
286
+ }
287
+ async function loadSelection(context) {
288
+ const pipelines = await loadGraph(context.config);
289
+ if (pipelines.length === 0) {
290
+ throw new IngestConfigError('No ingestion pipeline is exported. Export a definePipeline(...) value from the module configured as "entry".');
291
+ }
292
+ const tags = context.flags['--tag'];
293
+ return selectStreams(pipelines, Array.isArray(tags) ? tags : typeof tags === 'string' ? [tags] : []);
294
+ }
295
+ function openTarget(context) {
296
+ const clickhouse = context.config.clickhouse;
297
+ // Ingestion requires JSONEachRow and per-insert deduplication settings.
298
+ // The host executor contract does not guarantee either capability.
299
+ // Streams fetch, load and journal concurrently, so the executor must not be
300
+ // bound to one ClickHouse HTTP session (a session allows one in-flight query).
301
+ if (clickhouse) {
302
+ const executor = createStatelessClickHouseExecutor(clickhouse);
303
+ return { executor, database: clickhouse.database, targetId: targetIdOf(clickhouse.url, clickhouse.database), close: () => executor.close() };
304
+ }
305
+ throw new IngestConfigError('Ingestion requires a direct ClickHouse connection for JSON rows and deduplication settings. Configure clickhouse in your clickhouse.config.ts; a host-provided executor is not supported.');
306
+ }
307
+ function targetIdOf(url, database) {
308
+ try {
309
+ return `${new URL(url).host}/${database}`;
310
+ }
311
+ catch {
312
+ return `${url}/${database}`;
313
+ }
314
+ }
315
+ function parseBackfill(flags) {
316
+ const id = flags['--backfill'];
317
+ const from = parseTimestamp(flags['--from'], '--from');
318
+ const to = parseTimestamp(flags['--to'], '--to');
319
+ if (typeof id !== 'string') {
320
+ if (from || to)
321
+ throw new IngestConfigError('--from and --to require --backfill <id>: explicit ranges never touch the scheduled checkpoint.');
322
+ return undefined;
323
+ }
324
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(id))
325
+ throw new IngestConfigError(`Invalid --backfill id "${id}".`);
326
+ return { id, from, to };
327
+ }
328
+ function parseTimestamp(raw, flag) {
329
+ if (typeof raw !== 'string')
330
+ return undefined;
331
+ const date = new Date(raw);
332
+ if (Number.isNaN(date.getTime()))
333
+ throw new IngestConfigError(`Invalid timestamp for ${flag}: ${raw}`);
334
+ return date;
335
+ }
336
+ function parseMaxDuration(raw) {
337
+ if (typeof raw !== 'string')
338
+ return undefined;
339
+ const seconds = Number(raw);
340
+ if (!Number.isFinite(seconds) || seconds <= 0)
341
+ throw new IngestConfigError(`Invalid value for --max-duration: ${raw}`);
342
+ return seconds;
343
+ }
344
+ function describeStream(entry) {
345
+ return {
346
+ streamId: entry.stream.id,
347
+ pipelineId: entry.pipeline.id,
348
+ destination: `${entry.stream.destination.database}.${entry.stream.destination.name}`,
349
+ strategy: `${entry.stream.incremental.id}@${entry.stream.incremental.version}`,
350
+ tags: [...entry.effectiveTags],
351
+ };
352
+ }
353
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface BoundedQueue<T> {
2
+ /** Waits while the queue is full, which is what applies backpressure to the reader. */
3
+ push(item: T, signal: AbortSignal): Promise<void>;
4
+ /** Resolves `undefined` once the queue is closed and drained. */
5
+ pop(signal: AbortSignal): Promise<T | undefined>;
6
+ close(): void;
7
+ }
8
+ export declare function createBoundedQueue<T>(capacity: number): BoundedQueue<T>;
9
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,uFAAuF;IACvF,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,iEAAiE;IACjE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAChD,KAAK,IAAI,IAAI,CAAA;CACd;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CA+CvE"}
package/dist/queue.js ADDED
@@ -0,0 +1,47 @@
1
+ export function createBoundedQueue(capacity) {
2
+ const limit = Math.max(1, capacity);
3
+ const items = [];
4
+ let closed = false;
5
+ let wake = [];
6
+ const notify = () => {
7
+ const waiting = wake;
8
+ wake = [];
9
+ for (const resolve of waiting)
10
+ resolve();
11
+ };
12
+ const wait = (signal) => new Promise((resolve, reject) => {
13
+ const onAbort = () => reject(signal.reason);
14
+ signal.addEventListener('abort', onAbort, { once: true });
15
+ wake.push(() => {
16
+ signal.removeEventListener('abort', onAbort);
17
+ resolve();
18
+ });
19
+ });
20
+ return {
21
+ async push(item, signal) {
22
+ while (items.length >= limit) {
23
+ signal.throwIfAborted();
24
+ await wait(signal);
25
+ }
26
+ signal.throwIfAborted();
27
+ items.push(item);
28
+ notify();
29
+ },
30
+ async pop(signal) {
31
+ while (items.length === 0) {
32
+ if (closed)
33
+ return undefined;
34
+ signal.throwIfAborted();
35
+ await wait(signal);
36
+ }
37
+ const item = items.shift();
38
+ notify();
39
+ return item;
40
+ },
41
+ close() {
42
+ closed = true;
43
+ notify();
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,kBAAkB,CAAI,QAAgB;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACnC,MAAM,KAAK,GAAQ,EAAE,CAAA;IACrB,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,IAAI,IAAI,GAAsB,EAAE,CAAA;IAEhC,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,OAAO,GAAG,IAAI,CAAA;QACpB,IAAI,GAAG,EAAE,CAAA;QACT,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,EAAE,CAAA;IAC1C,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,CAAC,MAAmB,EAAE,EAAE,CACnC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACpC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YACb,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,OAAO,EAAE,CAAA;QACX,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEJ,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM;YACrB,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;gBAC7B,MAAM,CAAC,cAAc,EAAE,CAAA;gBACvB,MAAM,IAAI,CAAC,MAAM,CAAC,CAAA;YACpB,CAAC;YACD,MAAM,CAAC,cAAc,EAAE,CAAA;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,MAAM,EAAE,CAAA;QACV,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,MAAM;YACd,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,IAAI,MAAM;oBAAE,OAAO,SAAS,CAAA;gBAC5B,MAAM,CAAC,cAAc,EAAE,CAAA;gBACvB,MAAM,IAAI,CAAC,MAAM,CAAC,CAAA;YACpB,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;YAC1B,MAAM,EAAE,CAAA;YACR,OAAO,IAAI,CAAA;QACb,CAAC;QACD,KAAK;YACH,MAAM,GAAG,IAAI,CAAA;YACb,MAAM,EAAE,CAAA;QACV,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type ClickHouseExecutor } from '@chkit/clickhouse';
2
+ import { type JournalRow } from './journal.js';
3
+ import type { CommittedCheckpoint } from './types.js';
4
+ export interface JournalRepairPlan {
5
+ namespaceId: string;
6
+ sourceTable: string;
7
+ fingerprint: string;
8
+ healthy: boolean;
9
+ repairable: boolean;
10
+ problems: string[];
11
+ /** All physical rows in the source table, preserved in the archive. */
12
+ evidenceRows: number;
13
+ selectedEvidenceRows: number;
14
+ /** Valid selected facts plus all untouched rows from other namespaces. */
15
+ retainedFacts: number;
16
+ runs: number;
17
+ checkpoint: CommittedCheckpoint;
18
+ archiveTable: string;
19
+ replacementTable: string;
20
+ activation: string;
21
+ replayWarning: string;
22
+ }
23
+ export interface JournalRecoveryOptions {
24
+ executor: ClickHouseExecutor;
25
+ database: string;
26
+ targetId: string;
27
+ table?: string;
28
+ }
29
+ export interface JournalRepairResult {
30
+ applied: boolean;
31
+ /** Materializing a repair never changes the active journal configuration. */
32
+ activated: false;
33
+ plan: JournalRepairPlan;
34
+ }
35
+ /** Inspect valid run histories; overlapping runs are normal journal evidence. */
36
+ export declare function planJournalRepair(snapshot: readonly JournalRow[], namespaceId: string, sourceTable: string, targetId?: string): JournalRepairPlan;
37
+ /** Diagnose one complete source-table snapshot without modifying it. */
38
+ export declare function doctorJournal(namespaceId: string, options: JournalRecoveryOptions): Promise<JournalRepairPlan>;
39
+ /** Materialize a reviewed repair; activation is an explicit configuration change. */
40
+ export declare function repairJournal(namespaceId: string, fingerprint: string | undefined, options: JournalRecoveryOptions): Promise<JournalRepairResult>;
41
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,kBAAkB,EAA2B,MAAM,mBAAmB,CAAA;AAIlG,OAAO,EAAiE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAC7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAOrD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAA;IACpB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,mBAAmB,CAAA;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,6EAA6E;IAC7E,SAAS,EAAE,KAAK,CAAA;IAChB,IAAI,EAAE,iBAAiB,CAAA;CACxB;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,iBAAiB,CAuBnB;AAED,wEAAwE;AACxE,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAIpH;AAED,qFAAqF;AACrF,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CAoC9B"}
@@ -0,0 +1,145 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { waitForTable } from '@chkit/clickhouse';
3
+ import { IngestConfigError } from './errors.js';
4
+ import { validateJournalHistory } from './journal-history.js';
5
+ import { canonicalJson, DEFAULT_JOURNAL_TABLE, digest, journalTableSql } from './journal.js';
6
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
7
+ const READ_SETTINGS = {
8
+ select_sequential_consistency: '1', use_query_cache: 0, output_format_json_quote_64bit_integers: 1,
9
+ };
10
+ /** Inspect valid run histories; overlapping runs are normal journal evidence. */
11
+ export function planJournalRepair(snapshot, namespaceId, sourceTable, targetId) {
12
+ const selected = selectedRows(snapshot, namespaceId, targetId);
13
+ const validated = validateJournalHistory(selected, namespaceId);
14
+ const fingerprint = repairFingerprint(snapshot, namespaceId, sourceTable, targetId);
15
+ const suffix = fingerprint.slice(0, 24);
16
+ const replacementTable = `${sourceTable}_repair_${suffix}`;
17
+ return {
18
+ namespaceId,
19
+ sourceTable,
20
+ fingerprint,
21
+ healthy: validated.problems.length === 0,
22
+ repairable: true,
23
+ problems: validated.problems,
24
+ evidenceRows: snapshot.length,
25
+ selectedEvidenceRows: selected.length,
26
+ retainedFacts: snapshot.length - selected.length + validated.rows.length,
27
+ runs: new Set(selected.map((row) => row.run_id)).size,
28
+ checkpoint: validated.checkpoint,
29
+ archiveTable: `${sourceTable}_archive_${suffix}`,
30
+ replacementTable,
31
+ activation: `Stop all ingestion writers, materialize the reviewed repair, then configure ingest({ journalTable: '${replacementTable}' }) before restarting them.`,
32
+ replayWarning: 'The next run resumes from the selected valid checkpoint. Uncertain destination writes may be loaded again (at least once).',
33
+ };
34
+ }
35
+ /** Diagnose one complete source-table snapshot without modifying it. */
36
+ export async function doctorJournal(namespaceId, options) {
37
+ const table = recoveryTable(options);
38
+ const snapshot = await inspectSource(table, options);
39
+ return sourcePlan(snapshot, namespaceId, table, options.targetId);
40
+ }
41
+ /** Materialize a reviewed repair; activation is an explicit configuration change. */
42
+ export async function repairJournal(namespaceId, fingerprint, options) {
43
+ const table = recoveryTable(options);
44
+ const source = await inspectSource(table, options);
45
+ const plan = sourcePlan(source, namespaceId, table, options.targetId);
46
+ if (fingerprint === undefined)
47
+ return { applied: false, activated: false, plan };
48
+ if (fingerprint !== plan.fingerprint)
49
+ throw new IngestConfigError('Journal changed since review. Stop all writers, run ingest repair again, and review the new fingerprint.');
50
+ if (!plan.repairable)
51
+ throw new IngestConfigError(plan.problems.join(' '));
52
+ if (plan.healthy)
53
+ return { applied: false, activated: false, plan };
54
+ // Each attempt writes its own tables. Concurrent or interrupted repairs cannot
55
+ // change another attempt's verified output; none of them routes active writers.
56
+ const copyId = randomUUID().replaceAll('-', '').slice(0, 12);
57
+ const replacementTable = `${plan.replacementTable}_${copyId}`;
58
+ const materializedPlan = {
59
+ ...plan,
60
+ archiveTable: `${plan.archiveTable}_${copyId}`,
61
+ replacementTable,
62
+ activation: `Keep all ingestion writers stopped. Configure ingest({ journalTable: '${replacementTable}' }), then restart them. The active journal has not been changed.`,
63
+ };
64
+ await preserveSnapshot(materializedPlan.archiveTable, source.rows, options);
65
+ const selected = selectedRows(source.rows, namespaceId, options.targetId);
66
+ const validated = validateJournalHistory(selected, namespaceId);
67
+ const retained = [
68
+ ...source.rows.filter((row) => !isSelected(row, namespaceId, options.targetId)),
69
+ ...validated.rows,
70
+ ];
71
+ await preserveSnapshot(replacementTable, retained, options);
72
+ const copied = validateJournalHistory(selectedRows(await readSnapshot(replacementTable, options), namespaceId, options.targetId), namespaceId);
73
+ if (copied.problems.length > 0 || canonicalJson(copied.checkpoint) !== canonicalJson(plan.checkpoint)) {
74
+ throw new Error('Replacement journal verification failed; the active journal remains unchanged.');
75
+ }
76
+ const finalSnapshot = await readSnapshot(table, options);
77
+ if (repairFingerprint(finalSnapshot, namespaceId, table, options.targetId) !== fingerprint) {
78
+ throw new IngestConfigError('Journal changed while copying. Keep the original journal active, stop all writers, and review a new repair plan.');
79
+ }
80
+ return { applied: true, activated: false, plan: materializedPlan };
81
+ }
82
+ function recoveryTable(options) {
83
+ const table = options.table ?? DEFAULT_JOURNAL_TABLE;
84
+ identifier(options.database);
85
+ identifier(table);
86
+ return table;
87
+ }
88
+ async function inspectSource(table, options) {
89
+ const exists = await options.executor.query(`SELECT 1 AS x FROM system.tables WHERE database = ${sqlString(options.database)} AND name = ${sqlString(table)}`, READ_SETTINGS);
90
+ if (exists.length === 0)
91
+ return { rows: [], missing: true };
92
+ return { rows: await readSnapshot(table, options), missing: false };
93
+ }
94
+ function sourcePlan(snapshot, namespaceId, table, targetId) {
95
+ const plan = planJournalRepair(snapshot.rows, namespaceId, table, targetId);
96
+ if (!snapshot.missing)
97
+ return plan;
98
+ return {
99
+ ...plan, healthy: false, repairable: false,
100
+ problems: [...plan.problems, `Journal source table ${table} is missing. Restore its evidence before repairing, or run ingest on a fresh target.`],
101
+ };
102
+ }
103
+ async function readSnapshot(table, options) {
104
+ return options.executor.query(`SELECT * FROM ${qualified(options.database, table)} ORDER BY target_id, namespace_id, run_id, event_seq, event_id, payload_hash, event_at`, READ_SETTINGS);
105
+ }
106
+ async function preserveSnapshot(table, rows, options) {
107
+ await options.executor.command(journalTableSql(qualified(options.database, table)));
108
+ await waitForTable(options.executor, options.database, table);
109
+ if (rows.length > 0) {
110
+ await options.executor.insert({
111
+ table: `${options.database}.${table}`,
112
+ values: [...rows],
113
+ settings: { async_insert: 0, insert_deduplication_token: snapshotFingerprint(rows) },
114
+ });
115
+ }
116
+ const archived = await readSnapshot(table, options);
117
+ if (snapshotFingerprint(archived) !== snapshotFingerprint(rows)) {
118
+ throw new Error(`Evidence verification failed for ${table}; the active journal remains unchanged.`);
119
+ }
120
+ }
121
+ function selectedRows(rows, namespaceId, targetId) {
122
+ return rows.filter((row) => isSelected(row, namespaceId, targetId));
123
+ }
124
+ function isSelected(row, namespaceId, targetId) {
125
+ return row.namespace_id === namespaceId && (targetId === undefined || row.target_id === targetId);
126
+ }
127
+ function repairFingerprint(rows, namespaceId, table, targetId) {
128
+ return digest([namespaceId, table, targetId ?? '', snapshotFingerprint(rows)]);
129
+ }
130
+ function snapshotFingerprint(rows) {
131
+ return digest(rows.map((row) => canonicalJson(row)).sort());
132
+ }
133
+ function qualified(database, table) {
134
+ identifier(database);
135
+ identifier(table);
136
+ return `\`${database}\`.\`${table}\``;
137
+ }
138
+ function identifier(value) {
139
+ if (!IDENTIFIER.test(value))
140
+ throw new IngestConfigError(`Invalid journal identifier "${value}".`);
141
+ }
142
+ function sqlString(value) {
143
+ return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
144
+ }
145
+ //# sourceMappingURL=recovery.js.map