@chkit/plugin-ingest 0.2.0-beta.8
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/README.md +30 -0
- package/dist/destination.d.ts +40 -0
- package/dist/destination.d.ts.map +1 -0
- package/dist/destination.js +63 -0
- package/dist/destination.js.map +1 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +133 -0
- package/dist/errors.js.map +1 -0
- package/dist/executor.d.ts +30 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +538 -0
- package/dist/executor.js.map +1 -0
- package/dist/incremental.d.ts +45 -0
- package/dist/incremental.d.ts.map +1 -0
- package/dist/incremental.js +72 -0
- package/dist/incremental.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/journal.d.ts +44 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +222 -0
- package/dist/journal.js.map +1 -0
- package/dist/loader.d.ts +12 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +37 -0
- package/dist/loader.js.map +1 -0
- package/dist/paginate.d.ts +20 -0
- package/dist/paginate.d.ts.map +1 -0
- package/dist/paginate.js +29 -0
- package/dist/paginate.js.map +1 -0
- package/dist/plugin.d.ts +110 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +255 -0
- package/dist/plugin.js.map +1 -0
- package/dist/queue.d.ts +9 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +47 -0
- package/dist/queue.js.map +1 -0
- package/dist/registry.d.ts +48 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +109 -0
- package/dist/registry.js.map +1 -0
- package/dist/retry.d.ts +12 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +69 -0
- package/dist/retry.js.map +1 -0
- package/dist/testing.d.ts +16 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +46 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @chkit/plugin-ingest
|
|
2
|
+
|
|
3
|
+
Write TypeScript readers to load application API data into ClickHouse with [chkit](https://www.npmjs.com/package/chkit). Use an external scheduler for repeated runs.
|
|
4
|
+
|
|
5
|
+
The executor records progress after writes succeed. A retry can reread rows from the last committed checkpoint.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineConfig } from '@chkit/core'
|
|
9
|
+
import { ingest } from '@chkit/plugin-ingest'
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
entry: './src/chkit.ts',
|
|
13
|
+
plugins: [ingest()],
|
|
14
|
+
clickhouse: { url: process.env.CLICKHOUSE_URL ?? '' },
|
|
15
|
+
})
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
chkit ingest run --tag schedule:1h
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Documentation: https://chkit.obsessiondb.com/api-sync/
|
|
23
|
+
|
|
24
|
+
For raw storage, typed rows, and where to map fields, see [Destinations and transformations](https://chkit.obsessiondb.com/api-sync/destinations/).
|
|
25
|
+
|
|
26
|
+
Install the source-authoring skill for a coding agent:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npx skills add obsessiondb/chkit --skill chkit-ingestion
|
|
30
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ClickHouseExecutor } from '@chkit/clickhouse';
|
|
2
|
+
import { type ColumnDefinition, type TableDefinition } from '@chkit/core';
|
|
3
|
+
import type { DestinationAdapter, Row } from './types.js';
|
|
4
|
+
export declare const BATCH_ID_COLUMN = "_chkit_batch_id";
|
|
5
|
+
export declare const RUN_ID_COLUMN = "_chkit_run_id";
|
|
6
|
+
export declare const INGESTED_AT_COLUMN = "_chkit_ingested_at";
|
|
7
|
+
/**
|
|
8
|
+
* Runtime-owned metadata every ingestion destination table carries. Spread it
|
|
9
|
+
* into the table's `columns`. `_chkit_ingested_at` is destination-owned: it is
|
|
10
|
+
* never sent by the loader, so it records the physical publication time even
|
|
11
|
+
* when a retry recomputes it under the same deduplication token.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ingestionColumns: readonly ColumnDefinition[];
|
|
14
|
+
export type RawRow = {
|
|
15
|
+
id: string;
|
|
16
|
+
raw: unknown;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Landing table for provider objects exactly as received: a stable id plus the
|
|
20
|
+
* untouched object in a native JSON column. Typed shapes are derived from it
|
|
21
|
+
* inside ClickHouse (views or materialized views), so changing a transform
|
|
22
|
+
* never requires re-fetching the source. Replays and overlapping windows
|
|
23
|
+
* collapse to the latest ingested version of each id.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rawTable(input: {
|
|
26
|
+
database: string;
|
|
27
|
+
name: string;
|
|
28
|
+
comment?: string;
|
|
29
|
+
}): TableDefinition;
|
|
30
|
+
/** Shape provider objects for a {@link rawTable} without mapping their fields. */
|
|
31
|
+
export declare function rawRows<T>(items: readonly T[], id: (item: T) => string): RawRow[];
|
|
32
|
+
/**
|
|
33
|
+
* A successful synchronous insert response (or an awaited async insert) is the
|
|
34
|
+
* sink evidence ChKit trusts. A missing response stays ambiguous and is retried
|
|
35
|
+
* with the same token.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createClickHouseDestination(executor: ClickHouseExecutor): DestinationAdapter;
|
|
38
|
+
/** JSONEachRow encodes big integers as exact decimal strings, including nested values. */
|
|
39
|
+
export declare function toJsonRows(rows: readonly Row[]): Row[];
|
|
40
|
+
//# sourceMappingURL=destination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destination.d.ts","sourceRoot":"","sources":["../src/destination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAS,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AAEhF,OAAO,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AAEzD,eAAO,MAAM,eAAe,oBAAoB,CAAA;AAChD,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAC5C,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,gBAAgB,EAIvD,CAAA;AAGD,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,OAAO,CAAA;CACb,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,CAUrG;AAED,kFAAkF;AAClF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,EAAE,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAc5F;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,CAEtD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { table } from '@chkit/core';
|
|
2
|
+
export const BATCH_ID_COLUMN = '_chkit_batch_id';
|
|
3
|
+
export const RUN_ID_COLUMN = '_chkit_run_id';
|
|
4
|
+
export const INGESTED_AT_COLUMN = '_chkit_ingested_at';
|
|
5
|
+
/**
|
|
6
|
+
* Runtime-owned metadata every ingestion destination table carries. Spread it
|
|
7
|
+
* into the table's `columns`. `_chkit_ingested_at` is destination-owned: it is
|
|
8
|
+
* never sent by the loader, so it records the physical publication time even
|
|
9
|
+
* when a retry recomputes it under the same deduplication token.
|
|
10
|
+
*/
|
|
11
|
+
export const ingestionColumns = [
|
|
12
|
+
{ name: BATCH_ID_COLUMN, type: 'String' },
|
|
13
|
+
{ name: RUN_ID_COLUMN, type: 'String' },
|
|
14
|
+
{ name: INGESTED_AT_COLUMN, type: "DateTime64(6, 'UTC')", default: 'fn:now64(6)' },
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Landing table for provider objects exactly as received: a stable id plus the
|
|
18
|
+
* untouched object in a native JSON column. Typed shapes are derived from it
|
|
19
|
+
* inside ClickHouse (views or materialized views), so changing a transform
|
|
20
|
+
* never requires re-fetching the source. Replays and overlapping windows
|
|
21
|
+
* collapse to the latest ingested version of each id.
|
|
22
|
+
*/
|
|
23
|
+
export function rawTable(input) {
|
|
24
|
+
return table({
|
|
25
|
+
database: input.database,
|
|
26
|
+
name: input.name,
|
|
27
|
+
comment: input.comment,
|
|
28
|
+
columns: [{ name: 'id', type: 'String' }, { name: 'raw', type: 'JSON' }, ...ingestionColumns],
|
|
29
|
+
engine: `ReplacingMergeTree(${INGESTED_AT_COLUMN})`,
|
|
30
|
+
primaryKey: ['id'],
|
|
31
|
+
orderBy: ['id'],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Shape provider objects for a {@link rawTable} without mapping their fields. */
|
|
35
|
+
export function rawRows(items, id) {
|
|
36
|
+
return items.map((item) => ({ id: id(item), raw: item }));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A successful synchronous insert response (or an awaited async insert) is the
|
|
40
|
+
* sink evidence ChKit trusts. A missing response stays ambiguous and is retried
|
|
41
|
+
* with the same token.
|
|
42
|
+
*/
|
|
43
|
+
export function createClickHouseDestination(executor) {
|
|
44
|
+
return {
|
|
45
|
+
async insert({ table, rows, token }) {
|
|
46
|
+
if (rows.length === 0)
|
|
47
|
+
return;
|
|
48
|
+
await executor.insert({
|
|
49
|
+
table: `${table.database}.${table.name}`,
|
|
50
|
+
values: toJsonRows(rows),
|
|
51
|
+
settings: {
|
|
52
|
+
insert_deduplication_token: token,
|
|
53
|
+
wait_for_async_insert: 1,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** JSONEachRow encodes big integers as exact decimal strings, including nested values. */
|
|
60
|
+
export function toJsonRows(rows) {
|
|
61
|
+
return JSON.parse(JSON.stringify(rows, (_key, value) => typeof value === 'bigint' ? value.toString() : value));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=destination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destination.js","sourceRoot":"","sources":["../src/destination.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAA+C,MAAM,aAAa,CAAA;AAIhF,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAChD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAA;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAgC;IAC3D,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE;IACzC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE;IACvC,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,aAAa,EAAE;CACnF,CAAA;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,KAA2D;IAClF,OAAO,KAAK,CAAC;QACX,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,gBAAgB,CAAC;QAC7F,MAAM,EAAE,sBAAsB,kBAAkB,GAAG;QACnD,UAAU,EAAE,CAAC,IAAI,CAAC;QAClB,OAAO,EAAE,CAAC,IAAI,CAAC;KAChB,CAAC,CAAA;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,OAAO,CAAI,KAAmB,EAAE,EAAuB;IACrE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAA4B;IACtE,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;YACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YAC7B,MAAM,QAAQ,CAAC,MAAM,CAAC;gBACpB,KAAK,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;gBACxC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC;gBACxB,QAAQ,EAAE;oBACR,0BAA0B,EAAE,KAAK;oBACjC,qBAAqB,EAAE,CAAC;iBACzB;aACF,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,IAAoB;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAc,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAU,CAAA;AAClI,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ErrorClassifier, FailureClass } from './types.js';
|
|
2
|
+
export declare class IngestConfigError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Canonical boundary for fetch-based readers when `response.ok` is false.
|
|
7
|
+
* Keeps normalized HTTP facts plus the original Response for raw inspection.
|
|
8
|
+
*/
|
|
9
|
+
export declare class HttpError extends Error {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly statusText: string;
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly retryAfterMs: number | undefined;
|
|
14
|
+
readonly body: string;
|
|
15
|
+
readonly response: Response;
|
|
16
|
+
private constructor();
|
|
17
|
+
static fromResponse(response: Response): Promise<HttpError>;
|
|
18
|
+
}
|
|
19
|
+
/** Executor-owned failure that always preserves the exact thrown value. */
|
|
20
|
+
export declare class FetchFailure extends Error {
|
|
21
|
+
readonly classification: FailureClass;
|
|
22
|
+
readonly cause: unknown;
|
|
23
|
+
constructor(cause: unknown, classification: FailureClass);
|
|
24
|
+
}
|
|
25
|
+
export declare class BudgetExhausted extends Error {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Cancellation is authoritative, then the common HTTP/network fallback is
|
|
30
|
+
* derived, then an optional provider classifier may enrich or override it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function classifyFailure(cause: unknown, signal: AbortSignal, classifier: ErrorClassifier | undefined): FailureClass;
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAiB/D,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAE3B,OAAO;WAWM,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;CAIlE;AAED,2EAA2E;AAC3E,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAA;IACrC,SAAkB,KAAK,EAAE,OAAO,CAAA;gBAEpB,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY;CAMzD;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,eAAe,GAAG,SAAS,GACtC,YAAY,CAId"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const DIAGNOSTIC_BODY_LIMIT = 2048;
|
|
2
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
3
|
+
'ECONNRESET',
|
|
4
|
+
'ECONNREFUSED',
|
|
5
|
+
'ECONNABORTED',
|
|
6
|
+
'ETIMEDOUT',
|
|
7
|
+
'EPIPE',
|
|
8
|
+
'EAI_AGAIN',
|
|
9
|
+
'ENOTFOUND',
|
|
10
|
+
'UND_ERR_SOCKET',
|
|
11
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
12
|
+
'UND_ERR_HEADERS_TIMEOUT',
|
|
13
|
+
'UND_ERR_BODY_TIMEOUT',
|
|
14
|
+
]);
|
|
15
|
+
export class IngestConfigError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'IngestConfigError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Canonical boundary for fetch-based readers when `response.ok` is false.
|
|
23
|
+
* Keeps normalized HTTP facts plus the original Response for raw inspection.
|
|
24
|
+
*/
|
|
25
|
+
export class HttpError extends Error {
|
|
26
|
+
status;
|
|
27
|
+
statusText;
|
|
28
|
+
url;
|
|
29
|
+
retryAfterMs;
|
|
30
|
+
body;
|
|
31
|
+
response;
|
|
32
|
+
constructor(response, body) {
|
|
33
|
+
super(`HTTP ${response.status} ${response.statusText} for ${redactUrl(response.url)}${body ? `: ${body}` : ''}`);
|
|
34
|
+
this.name = 'HttpError';
|
|
35
|
+
this.status = response.status;
|
|
36
|
+
this.statusText = response.statusText;
|
|
37
|
+
this.url = redactUrl(response.url);
|
|
38
|
+
this.retryAfterMs = parseRetryAfter(response.headers.get('retry-after'));
|
|
39
|
+
this.body = body;
|
|
40
|
+
this.response = response;
|
|
41
|
+
}
|
|
42
|
+
static async fromResponse(response) {
|
|
43
|
+
const body = await readDiagnosticBody(response);
|
|
44
|
+
return new HttpError(response, body);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Executor-owned failure that always preserves the exact thrown value. */
|
|
48
|
+
export class FetchFailure extends Error {
|
|
49
|
+
classification;
|
|
50
|
+
cause;
|
|
51
|
+
constructor(cause, classification) {
|
|
52
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
53
|
+
this.name = 'FetchFailure';
|
|
54
|
+
this.cause = cause;
|
|
55
|
+
this.classification = classification;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class BudgetExhausted extends Error {
|
|
59
|
+
constructor(message) {
|
|
60
|
+
super(message);
|
|
61
|
+
this.name = 'BudgetExhausted';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Cancellation is authoritative, then the common HTTP/network fallback is
|
|
66
|
+
* derived, then an optional provider classifier may enrich or override it.
|
|
67
|
+
*/
|
|
68
|
+
export function classifyFailure(cause, signal, classifier) {
|
|
69
|
+
if (signal.aborted)
|
|
70
|
+
return { kind: 'cancelled' };
|
|
71
|
+
const fallback = classifyCommon(cause);
|
|
72
|
+
return classifier?.(cause, fallback) ?? fallback;
|
|
73
|
+
}
|
|
74
|
+
function classifyCommon(cause) {
|
|
75
|
+
if (cause instanceof HttpError) {
|
|
76
|
+
if (cause.status === 429)
|
|
77
|
+
return { kind: 'rate_limited', retryAfterMs: cause.retryAfterMs };
|
|
78
|
+
if (cause.status === 408 || cause.status === 425 || cause.status >= 500)
|
|
79
|
+
return { kind: 'transient' };
|
|
80
|
+
if (cause.status >= 400)
|
|
81
|
+
return { kind: 'permanent' };
|
|
82
|
+
return { kind: 'unknown' };
|
|
83
|
+
}
|
|
84
|
+
const code = errorCode(cause);
|
|
85
|
+
if (code !== undefined && TRANSIENT_NETWORK_CODES.has(code))
|
|
86
|
+
return { kind: 'transient' };
|
|
87
|
+
if (cause instanceof TypeError && /fetch failed|network|socket/i.test(cause.message))
|
|
88
|
+
return { kind: 'transient' };
|
|
89
|
+
// A request's own timeout/abort does not cancel the ingestion execution.
|
|
90
|
+
if (cause instanceof Error && (cause.name === 'TimeoutError' || cause.name === 'AbortError'))
|
|
91
|
+
return { kind: 'transient' };
|
|
92
|
+
return { kind: 'unknown' };
|
|
93
|
+
}
|
|
94
|
+
function errorCode(value) {
|
|
95
|
+
if (typeof value !== 'object' || value === null)
|
|
96
|
+
return undefined;
|
|
97
|
+
if ('code' in value && typeof value.code === 'string')
|
|
98
|
+
return value.code;
|
|
99
|
+
if ('cause' in value)
|
|
100
|
+
return errorCode(value.cause);
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
function parseRetryAfter(header) {
|
|
104
|
+
if (header === null)
|
|
105
|
+
return undefined;
|
|
106
|
+
const seconds = Number(header);
|
|
107
|
+
if (Number.isFinite(seconds))
|
|
108
|
+
return Math.max(0, seconds * 1000);
|
|
109
|
+
const date = Date.parse(header);
|
|
110
|
+
if (Number.isNaN(date))
|
|
111
|
+
return undefined;
|
|
112
|
+
return Math.max(0, date - Date.now());
|
|
113
|
+
}
|
|
114
|
+
async function readDiagnosticBody(response) {
|
|
115
|
+
try {
|
|
116
|
+
const text = await response.clone().text();
|
|
117
|
+
return text.length > DIAGNOSTIC_BODY_LIMIT ? `${text.slice(0, DIAGNOSTIC_BODY_LIMIT)}…` : text;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Query strings may carry API keys; keep only origin and path in diagnostics.
|
|
124
|
+
function redactUrl(url) {
|
|
125
|
+
try {
|
|
126
|
+
const parsed = new URL(url);
|
|
127
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return url;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SelectedStream } from './registry.js';
|
|
2
|
+
import type { DestinationAdapter, ExecutionResult, Journal } from './types.js';
|
|
3
|
+
export interface BackfillRequest {
|
|
4
|
+
/** Stable identity: resuming the same backfill reuses its isolated checkpoint namespace. */
|
|
5
|
+
id: string;
|
|
6
|
+
from: Date | undefined;
|
|
7
|
+
to: Date | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface ExecutionRequest {
|
|
10
|
+
selected: readonly SelectedStream[];
|
|
11
|
+
backfill: BackfillRequest | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface ExecutionEnv {
|
|
14
|
+
journal: Journal;
|
|
15
|
+
destination: DestinationAdapter;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
/** Host-default execution budget. Exhaustion preserves committed progress. */
|
|
18
|
+
maxDurationMs?: number;
|
|
19
|
+
/** Finite number of mapped batches buffered between fetch and load. */
|
|
20
|
+
prefetchBatches?: number;
|
|
21
|
+
now?: () => Date;
|
|
22
|
+
log?: (message: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One transient execution context for one exact stream selection. The run id
|
|
26
|
+
* correlates journal and telemetry evidence only: every stream plans and
|
|
27
|
+
* recovers independently from its own journal-backed checkpoint.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runIngestion(request: ExecutionRequest, input: ExecutionEnv): Promise<ExecutionResult>;
|
|
30
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,OAAO,KAAK,EAGV,kBAAkB,EAClB,eAAe,EACf,OAAO,EAOR,MAAM,YAAY,CAAA;AAiBnB,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;IACtB,EAAE,EAAE,IAAI,GAAG,SAAS,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,QAAQ,EAAE,eAAe,GAAG,SAAS,CAAA;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,kBAAkB,CAAA;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAoCD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAsC3G"}
|