@useairfoil/connector-kit 0.6.1 → 0.7.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.
- package/LICENSE.txt +201 -0
- package/METRICS.md +27 -30
- package/README.md +33 -51
- package/dist/{annotations-BE74YmAO.d.mts → annotations-vzSzXtr-.d.mts} +5 -10
- package/dist/{wings-I56hGtNv.mjs → arrow-flight-DDQ0ktx9.mjs} +25 -99
- package/dist/arrow-flight-DDQ0ktx9.mjs.map +1 -0
- package/dist/connector-app/index.d.mts +2 -2
- package/dist/connector-app/index.mjs +2 -2
- package/dist/connector-app-C6_dZnhE.mjs +256 -0
- package/dist/connector-app-C6_dZnhE.mjs.map +1 -0
- package/dist/{constants-D-GQzpHZ.mjs → constants-v3UX-hf6.mjs} +3 -2
- package/dist/constants-v3UX-hf6.mjs.map +1 -0
- package/dist/demo/index.d.mts +1 -1
- package/dist/demo/index.mjs +1 -1
- package/dist/{demo-B30Vlkk2.mjs → demo-DqTJ7TWI.mjs} +32 -19
- package/dist/demo-DqTJ7TWI.mjs.map +1 -0
- package/dist/iceberg/index.d.mts +3 -3
- package/dist/iceberg/index.mjs +3 -3
- package/dist/iceberg-Cj2PwlNm.mjs +30 -0
- package/dist/iceberg-Cj2PwlNm.mjs.map +1 -0
- package/dist/{index-DJhDp4Xc.d.mts → index-BTAbexBp.d.mts} +13 -16
- package/dist/{index-BUHSGm9Q.d.mts → index-Byr8NTdG.d.mts} +2 -2
- package/dist/{index-Dk7urvpF.d.mts → index-CV6V6fyx.d.mts} +9 -6
- package/dist/{index-NnCutIqM.d.mts → index-CZKx2Yce.d.mts} +29 -19
- package/dist/{index-BfXpgxk9.d.mts → index-DeTwIUKl.d.mts} +3 -3
- package/dist/{index-CRVAwJ5-.d.mts → index-dHS59EZl.d.mts} +4 -4
- package/dist/{index-ygpml2kC.d.mts → index-fHIJJoRe.d.mts} +17 -10
- package/dist/index-uFa2wwYo.d.mts +27 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +10 -10
- package/dist/ingestion/index.d.mts +1 -1
- package/dist/ingestion/index.mjs +1 -1
- package/dist/{ingestion-q0WwqUSt.mjs → ingestion-DoCUwm5N.mjs} +6 -6
- package/dist/{ingestion-q0WwqUSt.mjs.map → ingestion-DoCUwm5N.mjs.map} +1 -1
- package/dist/ingestor/index.d.mts +3 -3
- package/dist/ingestor/index.mjs +3 -3
- package/dist/{ingestor-D4L-HPkg.mjs → ingestor-DjCEzBzZ.mjs} +6 -6
- package/dist/ingestor-DjCEzBzZ.mjs.map +1 -0
- package/dist/{instrumented-Bhp7fyvh.mjs → instrumented-Bq1WX2L5.mjs} +2 -2
- package/dist/{instrumented-Bhp7fyvh.mjs.map → instrumented-Bq1WX2L5.mjs.map} +1 -1
- package/dist/manifest/index.mjs +1 -1
- package/dist/{manifest-BGKxmKLy.mjs → manifest-CABeIhAG.mjs} +2 -2
- package/dist/{manifest-BGKxmKLy.mjs.map → manifest-CABeIhAG.mjs.map} +1 -1
- package/dist/{metrics-BaEX__MN.mjs → metrics-C901IIYZ.mjs} +6 -11
- package/dist/metrics-C901IIYZ.mjs.map +1 -0
- package/dist/{metrics-CVXFL7YT.d.mts → metrics-DoJ0WeKu.d.mts} +2 -2
- package/dist/metrics.d.mts +1 -1
- package/dist/metrics.mjs +1 -1
- package/dist/runtime-config/index.d.mts +2 -2
- package/dist/runtime-config/index.mjs +3 -3
- package/dist/{runtime-config-Oa55gAew.mjs → runtime-config-Bn8Fivt1.mjs} +11 -8
- package/dist/runtime-config-Bn8Fivt1.mjs.map +1 -0
- package/dist/{server-CMnxz3h0.mjs → server-D1TTN0_D.mjs} +3 -3
- package/dist/{server-CMnxz3h0.mjs.map → server-D1TTN0_D.mjs.map} +1 -1
- package/dist/{service-DLkhuxYl.d.mts → service-BRoy_EId.d.mts} +2 -2
- package/dist/state-store/index.mjs +1 -1
- package/dist/{state-store-uFvHY7Ax.mjs → state-store-DhSrKVEH.mjs} +2 -2
- package/dist/{state-store-uFvHY7Ax.mjs.map → state-store-DhSrKVEH.mjs.map} +1 -1
- package/dist/status/index.d.mts +1 -1
- package/dist/status/index.mjs +1 -1
- package/dist/{status-CKb9IhQ9.mjs → status-4h6Oxh-O.mjs} +2 -2
- package/dist/{status-CKb9IhQ9.mjs.map → status-4h6Oxh-O.mjs.map} +1 -1
- package/dist/{table-V0tgUC5i.mjs → table-CxM09xf7.mjs} +22 -85
- package/dist/table-CxM09xf7.mjs.map +1 -0
- package/dist/{types-xZtSmrGA.d.mts → types-NtSv6gwl.d.mts} +3 -3
- package/dist/webhook/index.d.mts +2 -2
- package/dist/webhook/index.mjs +1 -1
- package/package.json +19 -18
- package/src/catalog/retry.ts +25 -0
- package/src/catalog/table.ts +73 -37
- package/src/connector-app/create-table.ts +8 -8
- package/src/connector-app/demo.ts +24 -19
- package/src/connector-app/index.ts +1 -0
- package/src/connector-app/setup.ts +69 -0
- package/src/core/types.ts +1 -1
- package/src/demo/definition.ts +1 -1
- package/src/demo/generate.ts +1 -1
- package/src/demo/run.ts +60 -30
- package/src/iceberg/annotations.ts +3 -16
- package/src/iceberg/index.ts +2 -2
- package/src/iceberg/table.ts +28 -78
- package/src/ingestion/engine.ts +1 -1
- package/src/ingestor/{wings.ts → arrow-flight.ts} +25 -27
- package/src/ingestor/console.ts +1 -1
- package/src/ingestor/index.ts +5 -1
- package/src/runtime-config/config.ts +8 -3
- package/src/runtime-config/constants.ts +2 -1
- package/src/runtime-config/index.ts +1 -1
- package/src/runtime-config/table-bindings.ts +6 -4
- package/src/telemetry.ts +11 -19
- package/dist/connector-app-C-L4QqG6.mjs +0 -123
- package/dist/connector-app-C-L4QqG6.mjs.map +0 -1
- package/dist/constants-D-GQzpHZ.mjs.map +0 -1
- package/dist/demo-B30Vlkk2.mjs.map +0 -1
- package/dist/iceberg-D6L8I6vA.mjs +0 -39
- package/dist/iceberg-D6L8I6vA.mjs.map +0 -1
- package/dist/index-CUPP2A_m.d.mts +0 -27
- package/dist/ingestor-D4L-HPkg.mjs.map +0 -1
- package/dist/metrics-BaEX__MN.mjs.map +0 -1
- package/dist/runtime-config-Oa55gAew.mjs.map +0 -1
- package/dist/table-V0tgUC5i.mjs.map +0 -1
- package/dist/wings-I56hGtNv.mjs.map +0 -1
package/src/iceberg/table.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
CreateTableRequest,
|
|
3
3
|
IcebergType,
|
|
4
4
|
MapType,
|
|
5
5
|
StructField,
|
|
@@ -12,11 +12,10 @@ import { Effect, Schema, type SchemaRepresentation } from "effect";
|
|
|
12
12
|
import { IcebergSchemaError } from "./error";
|
|
13
13
|
|
|
14
14
|
/** Options for creating a table. */
|
|
15
|
-
export type
|
|
15
|
+
export type CreateTableOptions = {
|
|
16
|
+
readonly name: string;
|
|
16
17
|
/** Where the table's files go. The catalog may choose this when omitted. */
|
|
17
18
|
readonly location?: string | undefined;
|
|
18
|
-
/** UUID for the table. Random by default. */
|
|
19
|
-
readonly uuid?: string | undefined;
|
|
20
19
|
/** Extra table properties. These win over the ones from the schema. */
|
|
21
20
|
readonly properties?: Readonly<Record<string, string>> | undefined;
|
|
22
21
|
};
|
|
@@ -35,9 +34,6 @@ export type CompiledTable = {
|
|
|
35
34
|
readonly description: string | undefined;
|
|
36
35
|
};
|
|
37
36
|
|
|
38
|
-
// Iceberg reserves its highest 200 field IDs.
|
|
39
|
-
const maxFieldId = 2_147_483_447;
|
|
40
|
-
|
|
41
37
|
const anonymousDeclaration =
|
|
42
38
|
"Schemas without a name, such as Schema.Class, do not work here; use Schema.Struct";
|
|
43
39
|
|
|
@@ -121,7 +117,8 @@ class Compiler {
|
|
|
121
117
|
readonly #activeObjects = new Set<SchemaRepresentation.Objects>();
|
|
122
118
|
readonly #activeReferences = new Set<string>();
|
|
123
119
|
readonly #activeSuspends = new Set<SchemaRepresentation.Suspend>();
|
|
124
|
-
|
|
120
|
+
// The REST request needs IDs. Only the catalog's returned IDs are authoritative.
|
|
121
|
+
#nextId = 0;
|
|
125
122
|
|
|
126
123
|
constructor(private readonly references: SchemaRepresentation.References) {}
|
|
127
124
|
|
|
@@ -156,35 +153,6 @@ class Compiler {
|
|
|
156
153
|
});
|
|
157
154
|
}
|
|
158
155
|
|
|
159
|
-
#readId(annotations: Annotations | undefined, path: string, source: string): number {
|
|
160
|
-
const id = annotations?.fieldId;
|
|
161
|
-
|
|
162
|
-
if (typeof id !== "number") {
|
|
163
|
-
return fail(path, `Missing fieldId; add it with ${source}`);
|
|
164
|
-
}
|
|
165
|
-
if (!Number.isInteger(id)) return fail(path, "fieldId must be a whole number");
|
|
166
|
-
if (id < 1 || id > maxFieldId) {
|
|
167
|
-
return fail(path, `fieldId must be between 1 and ${maxFieldId}`);
|
|
168
|
-
}
|
|
169
|
-
const previousPath = this.#fieldIds.get(id);
|
|
170
|
-
if (previousPath !== undefined) {
|
|
171
|
-
return fail(path, `fieldId ${id} is already used at ${previousPath}`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
this.#fieldIds.set(id, path);
|
|
175
|
-
return id;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
#readNodeId(representation: Representation, path: string): number {
|
|
179
|
-
return this.#withReference(representation, path, (resolved) =>
|
|
180
|
-
this.#readId(
|
|
181
|
-
resolveAnnotations(resolved),
|
|
182
|
-
path,
|
|
183
|
-
".annotate({ fieldId }) on the schema itself",
|
|
184
|
-
),
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
156
|
#withReference<A>(
|
|
189
157
|
representation: Representation,
|
|
190
158
|
path: string,
|
|
@@ -231,7 +199,7 @@ class Compiler {
|
|
|
231
199
|
|
|
232
200
|
const fieldPath = propertyPath(path, property.name);
|
|
233
201
|
this.#checkRecursion(property.type, fieldPath);
|
|
234
|
-
const id = this.#
|
|
202
|
+
const id = ++this.#nextId;
|
|
235
203
|
const compiled = this.#compileType(property.type, fieldPath);
|
|
236
204
|
const fieldDescription = description(property.annotations);
|
|
237
205
|
|
|
@@ -332,7 +300,7 @@ class Compiler {
|
|
|
332
300
|
|
|
333
301
|
const elementPath = `${path}.element`;
|
|
334
302
|
const elementRepresentation = representation.rest[0]!;
|
|
335
|
-
const elementId = this.#
|
|
303
|
+
const elementId = ++this.#nextId;
|
|
336
304
|
const compiled = this.#compileType(elementRepresentation, elementPath);
|
|
337
305
|
|
|
338
306
|
return {
|
|
@@ -375,12 +343,12 @@ class Compiler {
|
|
|
375
343
|
const valuePath = `${path}.value`;
|
|
376
344
|
const keyRepresentation = representation.typeParameters[0]!;
|
|
377
345
|
const valueRepresentation = representation.typeParameters[1]!;
|
|
378
|
-
const keyId = this.#
|
|
346
|
+
const keyId = ++this.#nextId;
|
|
379
347
|
const compiledKey = this.#compileType(keyRepresentation, keyPath);
|
|
380
348
|
|
|
381
349
|
if (compiledKey.nullable) return fail(keyPath, "Map keys cannot be null");
|
|
382
350
|
|
|
383
|
-
const valueId = this.#
|
|
351
|
+
const valueId = ++this.#nextId;
|
|
384
352
|
const compiledValue = this.#compileType(valueRepresentation, valuePath);
|
|
385
353
|
|
|
386
354
|
return {
|
|
@@ -448,42 +416,23 @@ export const compileTable = (
|
|
|
448
416
|
}),
|
|
449
417
|
});
|
|
450
418
|
|
|
451
|
-
export const
|
|
419
|
+
export const makeCreateRequest = (
|
|
452
420
|
table: CompiledTable,
|
|
453
|
-
options:
|
|
454
|
-
):
|
|
455
|
-
|
|
421
|
+
options: CreateTableOptions,
|
|
422
|
+
): CreateTableRequest => ({
|
|
423
|
+
name: options.name,
|
|
424
|
+
schema: table.schema,
|
|
425
|
+
...(options.location === undefined ? {} : { location: options.location }),
|
|
426
|
+
properties: {
|
|
456
427
|
...(table.description === undefined ? {} : { comment: table.description }),
|
|
457
428
|
...options.properties,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
requirements: [{ type: "assert-create" }],
|
|
462
|
-
updates: [
|
|
463
|
-
{ action: "assign-uuid", uuid: options.uuid ?? crypto.randomUUID() },
|
|
464
|
-
{ action: "upgrade-format-version", "format-version": 2 },
|
|
465
|
-
{ action: "add-schema", schema: table.schema },
|
|
466
|
-
{ action: "set-current-schema", "schema-id": -1 },
|
|
467
|
-
{ action: "add-spec", spec: { "spec-id": 0, fields: [] } },
|
|
468
|
-
{ action: "set-default-spec", "spec-id": -1 },
|
|
469
|
-
{ action: "add-sort-order", "sort-order": { "order-id": 0, fields: [] } },
|
|
470
|
-
{ action: "set-default-sort-order", "sort-order-id": -1 },
|
|
471
|
-
...(options.location === undefined
|
|
472
|
-
? []
|
|
473
|
-
: [{ action: "set-location" as const, location: options.location }]),
|
|
474
|
-
...(Object.keys(properties).length === 0
|
|
475
|
-
? []
|
|
476
|
-
: [{ action: "set-properties" as const, updates: properties }]),
|
|
477
|
-
],
|
|
478
|
-
};
|
|
479
|
-
};
|
|
429
|
+
"format-version": "3",
|
|
430
|
+
},
|
|
431
|
+
});
|
|
480
432
|
|
|
481
433
|
/**
|
|
482
|
-
* Builds
|
|
483
|
-
*
|
|
484
|
-
* The field IDs come from the schema, so the table has to keep them. Send the
|
|
485
|
-
* commit with `commitTable`. Then load the table and check the IDs before you
|
|
486
|
-
* write any rows. Don't use `createTable` here, because it can change the IDs.
|
|
434
|
+
* Builds a REST create-table request from an Effect schema.
|
|
435
|
+
* The catalog assigns the stored field IDs.
|
|
487
436
|
*
|
|
488
437
|
* A root `description` becomes the table `comment`, and a field `description`
|
|
489
438
|
* becomes its `doc`. `title` and `examples` are dropped.
|
|
@@ -494,18 +443,19 @@ export const makeCommitRequest = (
|
|
|
494
443
|
* @example
|
|
495
444
|
* ```ts
|
|
496
445
|
* const Row = Schema.Struct({
|
|
497
|
-
* id: Schema.String.pipe(
|
|
446
|
+
* id: Schema.String.pipe(Schema.annotateKey({ description: "Row identifier" })),
|
|
498
447
|
* })
|
|
499
448
|
* .annotate({ description: "Connector rows" })
|
|
500
449
|
* .pipe(Iceberg.identifier("id"))
|
|
501
450
|
*
|
|
502
|
-
* const request = yield* Iceberg.
|
|
451
|
+
* const request = yield* Iceberg.makeCreateTableRequest(Row, {
|
|
452
|
+
* name: "rows",
|
|
503
453
|
* location: "s3://warehouse/rows"
|
|
504
454
|
* })
|
|
505
455
|
* ```
|
|
506
456
|
*/
|
|
507
|
-
export const
|
|
457
|
+
export const makeCreateTableRequest = (
|
|
508
458
|
schema: Schema.Top,
|
|
509
|
-
options:
|
|
510
|
-
): Effect.Effect<
|
|
511
|
-
Effect.map(compileTable(schema), (table) =>
|
|
459
|
+
options: CreateTableOptions,
|
|
460
|
+
): Effect.Effect<CreateTableRequest, IcebergSchemaError> =>
|
|
461
|
+
Effect.map(compileTable(schema), (table) => makeCreateRequest(table, options));
|
package/src/ingestion/engine.ts
CHANGED
|
@@ -480,7 +480,7 @@ const runChanges = Effect.fnUntraced(function* (
|
|
|
480
480
|
}).pipe(recordResourceError(connector, resource, "changes", "ingest"));
|
|
481
481
|
|
|
482
482
|
const lastSuccessAt = yield* DateTime.now.pipe(Effect.map(DateTime.formatIso));
|
|
483
|
-
// Advance the durable cursor only after
|
|
483
|
+
// Advance the durable cursor only after the engine acknowledges the page.
|
|
484
484
|
yield* Effect.withSpan(
|
|
485
485
|
store
|
|
486
486
|
.setChangesState(resource.name, {
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { RecordBatch, TypeMap } from "apache-arrow";
|
|
2
2
|
import type { TableIdentifier } from "iceberg-js";
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import { CatalogManager } from "@useairfoil/api";
|
|
5
5
|
import { Config, Effect, Layer } from "effect";
|
|
6
6
|
import { FetchHttpClient } from "effect/http";
|
|
7
7
|
|
|
8
8
|
import type { TableSource } from "../core/types";
|
|
9
9
|
|
|
10
|
-
import { ensureTable } from "../catalog/table";
|
|
11
10
|
import { ConnectorError } from "../errors";
|
|
11
|
+
import { compileTable } from "../iceberg/table";
|
|
12
12
|
import * as RuntimeConfig from "../runtime-config";
|
|
13
13
|
import { makeRowEncoder, type RowEncoder } from "./arrow";
|
|
14
14
|
import { Ingestor, type IngestorService } from "./service";
|
|
15
15
|
|
|
16
16
|
/** One catalog and an Iceberg table for each resource that runs. */
|
|
17
|
-
export type
|
|
17
|
+
export type ArrowFlightIngestorConfig<Source extends TableSource = TableSource> = {
|
|
18
18
|
readonly connector: Source;
|
|
19
19
|
readonly catalog: string;
|
|
20
20
|
readonly tables: RuntimeConfig.ResolvedTableBindings<Source>;
|
|
@@ -33,17 +33,13 @@ type PreparedTable = {
|
|
|
33
33
|
|
|
34
34
|
const connectorError = (message: string, cause?: unknown) => new ConnectorError({ message, cause });
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
export const
|
|
38
|
-
config:
|
|
39
|
-
): Layer.Layer<Ingestor, ConnectorError,
|
|
36
|
+
/** Compiles local schemas and opens one Arrow Flight stream per bound resource. */
|
|
37
|
+
export const layerArrowFlight = <const Source extends TableSource>(
|
|
38
|
+
config: ArrowFlightIngestorConfig<Source>,
|
|
39
|
+
): Layer.Layer<Ingestor, ConnectorError, CatalogManager.CatalogManager> =>
|
|
40
40
|
Layer.effect(Ingestor)(
|
|
41
41
|
Effect.gen(function* () {
|
|
42
|
-
const manager = yield*
|
|
43
|
-
const catalog = yield* manager
|
|
44
|
-
.getIcebergCatalog(config.catalog)
|
|
45
|
-
.pipe(Effect.mapError((cause) => connectorError("Failed to load Wings catalog", cause)));
|
|
46
|
-
|
|
42
|
+
const manager = yield* CatalogManager.CatalogManager;
|
|
47
43
|
const bindings: Partial<Record<string, RuntimeConfig.TableBinding>> = config.tables;
|
|
48
44
|
|
|
49
45
|
const prepared: Array<PreparedTable> = [];
|
|
@@ -51,15 +47,11 @@ export const layerWings = <const Source extends TableSource>(
|
|
|
51
47
|
const binding = bindings[resource.name];
|
|
52
48
|
if (!binding) continue;
|
|
53
49
|
|
|
54
|
-
const { namespace, name
|
|
50
|
+
const { namespace, name } = binding;
|
|
55
51
|
const identifier = { namespace, name };
|
|
56
|
-
const schema = yield*
|
|
57
|
-
catalog,
|
|
58
|
-
identifier,
|
|
59
|
-
location,
|
|
60
|
-
}).pipe(
|
|
52
|
+
const { schema } = yield* compileTable(resource.rowSchema).pipe(
|
|
61
53
|
Effect.mapError((cause) =>
|
|
62
|
-
connectorError(`
|
|
54
|
+
connectorError(`Invalid schema for ${resource.name}: ${cause.message}`, cause),
|
|
63
55
|
),
|
|
64
56
|
);
|
|
65
57
|
|
|
@@ -118,26 +110,32 @@ export const layerWings = <const Source extends TableSource>(
|
|
|
118
110
|
}),
|
|
119
111
|
);
|
|
120
112
|
|
|
121
|
-
/**
|
|
122
|
-
export const
|
|
113
|
+
/** Configures Arrow Flight ingestion from the hosted URL, catalog and table bindings. */
|
|
114
|
+
export const layerArrowFlightConfig = (connector: TableSource) =>
|
|
123
115
|
Layer.unwrap(
|
|
124
116
|
Effect.gen(function* () {
|
|
125
|
-
const { catalog, uri } = yield* Config.all({
|
|
117
|
+
const { catalog, uri, flightUri } = yield* Config.all({
|
|
126
118
|
catalog: RuntimeConfig.catalog,
|
|
127
|
-
uri: RuntimeConfig.
|
|
128
|
-
|
|
119
|
+
uri: RuntimeConfig.engineUri,
|
|
120
|
+
flightUri: RuntimeConfig.flightUri,
|
|
121
|
+
}).pipe(
|
|
122
|
+
Effect.mapError((cause) => connectorError("Invalid Arrow Flight configuration", cause)),
|
|
123
|
+
);
|
|
129
124
|
|
|
130
125
|
if (uri.protocol !== "http:" && uri.protocol !== "https:") {
|
|
131
|
-
return yield* Effect.fail(connectorError("Invalid
|
|
126
|
+
return yield* Effect.fail(connectorError("Invalid Arrow Flight URL"));
|
|
127
|
+
}
|
|
128
|
+
if (flightUri.protocol !== "http:" && flightUri.protocol !== "https:") {
|
|
129
|
+
return yield* Effect.fail(connectorError("Invalid Arrow Flight URL"));
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
const tables = yield* RuntimeConfig.loadTableBindings(connector).pipe(
|
|
135
133
|
Effect.mapError((cause) => connectorError(cause.message, cause)),
|
|
136
134
|
);
|
|
137
|
-
const manager =
|
|
135
|
+
const manager = CatalogManager.layer({ engineUri: uri.href, flightUri: flightUri.href }).pipe(
|
|
138
136
|
Layer.provide(FetchHttpClient.layer),
|
|
139
137
|
);
|
|
140
138
|
|
|
141
|
-
return
|
|
139
|
+
return layerArrowFlight({ connector, catalog, tables }).pipe(Layer.provide(manager));
|
|
142
140
|
}),
|
|
143
141
|
);
|
package/src/ingestor/console.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Effect, Layer } from "effect";
|
|
|
2
2
|
|
|
3
3
|
import { Ingestor } from "./service";
|
|
4
4
|
|
|
5
|
-
/** Logs batches without sending them
|
|
5
|
+
/** Logs batches without sending them over Arrow Flight. */
|
|
6
6
|
export const layerConsole = Layer.succeed(Ingestor)({
|
|
7
7
|
ingest: ({ resource, source, batch }) =>
|
|
8
8
|
Effect.logInfo(`[ingestor] -> Source: ${source} | Resource: ${resource}`).pipe(
|
package/src/ingestor/index.ts
CHANGED
|
@@ -6,4 +6,8 @@ export {
|
|
|
6
6
|
type IngestResult,
|
|
7
7
|
type IngestSource,
|
|
8
8
|
} from "./service";
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
layerArrowFlight,
|
|
11
|
+
layerArrowFlightConfig,
|
|
12
|
+
type ArrowFlightIngestorConfig,
|
|
13
|
+
} from "./arrow-flight";
|
|
@@ -7,8 +7,13 @@ export const httpPort = Config.Port(PlatformRuntimeKey.httpPort).pipe(
|
|
|
7
7
|
Config.withDefault(PlatformRuntimeDefault.httpPort),
|
|
8
8
|
);
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
export const
|
|
10
|
+
/** Engine URL, including the protocol. */
|
|
11
|
+
export const engineUri = Config.URL(PlatformRuntimeKey.engineUri);
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** Arrow Flight URL. Uses the engine URL when unset. */
|
|
14
|
+
export const flightUri = engineUri.pipe(
|
|
15
|
+
Config.flatMap((uri) => Config.URL(PlatformRuntimeKey.flightUri).pipe(Config.withDefault(uri))),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
/** Catalog ID. */
|
|
14
19
|
export const catalog = Config.NonEmptyString(PlatformRuntimeKey.catalog);
|
|
@@ -8,7 +8,8 @@ export const PlatformRuntimeKey = {
|
|
|
8
8
|
connectorInstanceId: "AIRFOIL_CONNECTOR_INSTANCE_ID",
|
|
9
9
|
stateTable: "AIRFOIL_STATE_TABLE",
|
|
10
10
|
postgresConnectionString: "POSTGRES_CONNECTION_STRING",
|
|
11
|
-
|
|
11
|
+
engineUri: "ENGINE_URI",
|
|
12
|
+
flightUri: "FLIGHT_URI",
|
|
12
13
|
catalog: "AIRFOIL_CATALOG",
|
|
13
14
|
otelEnabled: "OTEL_ENABLED",
|
|
14
15
|
otelExporterOtlpEndpoint: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { RuntimeConfigError, type RuntimeConfigErrorCode } from "./error";
|
|
2
|
-
export { catalog,
|
|
2
|
+
export { catalog, engineUri, flightUri, httpPort } from "./config";
|
|
3
3
|
export { PlatformRuntimeDefault, PlatformRuntimeKey } from "./constants";
|
|
4
4
|
export { layerHosted } from "./layer";
|
|
5
5
|
export { type ResolvedTableBindings, type TableBinding, loadTableBindings } from "./table-bindings";
|
|
@@ -2,15 +2,17 @@ import type { TableIdentifier } from "iceberg-js";
|
|
|
2
2
|
|
|
3
3
|
import { Config, Effect, Schema } from "effect";
|
|
4
4
|
|
|
5
|
-
import type { TableSource } from "../core/types";
|
|
6
|
-
|
|
7
5
|
import { PlatformRuntimeKey } from "./constants";
|
|
8
6
|
import { RuntimeConfigError } from "./error";
|
|
9
7
|
|
|
8
|
+
type BindingSource = {
|
|
9
|
+
readonly resources: ReadonlyArray<{ readonly name: string }>;
|
|
10
|
+
};
|
|
11
|
+
|
|
10
12
|
/** An Iceberg table and its storage location for each connector resource. */
|
|
11
13
|
export type TableBinding = TableIdentifier & { readonly location?: string | undefined };
|
|
12
14
|
|
|
13
|
-
export type ResolvedTableBindings<Source extends
|
|
15
|
+
export type ResolvedTableBindings<Source extends BindingSource> = Readonly<
|
|
14
16
|
Partial<Record<Source["resources"][number]["name"], TableBinding>>
|
|
15
17
|
>;
|
|
16
18
|
|
|
@@ -31,7 +33,7 @@ const fail = (code: "TABLE_BINDINGS_INVALID" | "TABLE_BINDINGS_MISMATCH", messag
|
|
|
31
33
|
new RuntimeConfigError({ code, message, cause: new Error(message) });
|
|
32
34
|
|
|
33
35
|
/** Reads hosted table bindings and checks they name resources of the connector. */
|
|
34
|
-
export const loadTableBindings = <const Source extends
|
|
36
|
+
export const loadTableBindings = <const Source extends BindingSource>(
|
|
35
37
|
connector: Source,
|
|
36
38
|
): Effect.Effect<ResolvedTableBindings<Source>, RuntimeConfigError> =>
|
|
37
39
|
Effect.gen(function* () {
|
package/src/telemetry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Config, Effect, Layer, Metric, Option, type Duration } from "effect";
|
|
1
|
+
import { Cause, Config, Effect, Layer, Metric, Option, Schema, type Duration } from "effect";
|
|
2
2
|
import { Headers } from "effect/http";
|
|
3
3
|
import * as Observability from "effect/observability";
|
|
4
4
|
|
|
@@ -60,7 +60,14 @@ const maxErrorMessageLength = 500;
|
|
|
60
60
|
const OtlpEnvConfig = Config.all({
|
|
61
61
|
enabled: Config.Boolean(PlatformRuntimeKey.otelEnabled).pipe(Config.withDefault(false)),
|
|
62
62
|
baseUrl: Config.option(Config.String(PlatformRuntimeKey.otelExporterOtlpEndpoint)),
|
|
63
|
-
|
|
63
|
+
headers: Config.option(
|
|
64
|
+
Config.Record(
|
|
65
|
+
Schema.String,
|
|
66
|
+
Schema.StringFromUriComponent,
|
|
67
|
+
PlatformRuntimeKey.otelExporterOtlpHeaders,
|
|
68
|
+
{ trim: true },
|
|
69
|
+
),
|
|
70
|
+
),
|
|
64
71
|
});
|
|
65
72
|
|
|
66
73
|
const defaultRedactedHeaders: ReadonlyArray<string | RegExp> = [
|
|
@@ -74,19 +81,6 @@ const defaultRedactedHeaders: ReadonlyArray<string | RegExp> = [
|
|
|
74
81
|
/token/i,
|
|
75
82
|
];
|
|
76
83
|
|
|
77
|
-
const parseOtelHeaders = (value: string): Record<string, string> =>
|
|
78
|
-
Object.fromEntries(
|
|
79
|
-
value
|
|
80
|
-
.split(",")
|
|
81
|
-
.map((entry) => entry.trim())
|
|
82
|
-
.filter(Boolean)
|
|
83
|
-
.flatMap((entry) => {
|
|
84
|
-
const separator = entry.indexOf("=");
|
|
85
|
-
if (separator < 1) return [];
|
|
86
|
-
return [[entry.slice(0, separator).trim(), entry.slice(separator + 1).trim()]];
|
|
87
|
-
}),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
84
|
const appendPath = (baseUrl: string, path: string) =>
|
|
91
85
|
`${baseUrl.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
92
86
|
|
|
@@ -254,9 +248,7 @@ export const layerOtlpTracing = (options: OtlpTracingOptions = {}) =>
|
|
|
254
248
|
{
|
|
255
249
|
enabled: raw.enabled,
|
|
256
250
|
endpoint: Option.getOrUndefined(raw.baseUrl),
|
|
257
|
-
headers: Option.
|
|
258
|
-
? parseOtelHeaders(raw.rawHeaders.value)
|
|
259
|
-
: undefined,
|
|
251
|
+
headers: Option.getOrUndefined(raw.headers),
|
|
260
252
|
},
|
|
261
253
|
options,
|
|
262
254
|
);
|
|
@@ -276,7 +268,7 @@ export const layerOtlpMetrics = (options: OtlpMetricsOptions = {}) =>
|
|
|
276
268
|
);
|
|
277
269
|
return Observability.OtlpMetrics.layer({
|
|
278
270
|
url: appendPath(endpoint, "/v1/metrics"),
|
|
279
|
-
headers: Option.
|
|
271
|
+
headers: Option.getOrUndefined(raw.headers),
|
|
280
272
|
exportInterval: options.exportInterval,
|
|
281
273
|
temporality: "cumulative",
|
|
282
274
|
}).pipe(Layer.provide(Observability.OtlpSerialization.layerJson));
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { n as layerWingsConfig, r as createTableFromWings } from "./wings-I56hGtNv.mjs";
|
|
3
|
-
import { s as wingsUri } from "./runtime-config-Oa55gAew.mjs";
|
|
4
|
-
import { n as run } from "./demo-B30Vlkk2.mjs";
|
|
5
|
-
import { n as run$1 } from "./ingestion-q0WwqUSt.mjs";
|
|
6
|
-
import { ByteSize, DateTime, Effect, Option, Tuple } from "effect";
|
|
7
|
-
import { Argument, CliError, Command, Flag } from "effect/cli";
|
|
8
|
-
import "effect/http";
|
|
9
|
-
import { NodeHttpServer } from "@effect/platform-node";
|
|
10
|
-
import { createServer } from "node:http";
|
|
11
|
-
//#region src/connector-app/check.ts
|
|
12
|
-
const ok = { _tag: "ok" };
|
|
13
|
-
const error = (message) => ({
|
|
14
|
-
_tag: "error",
|
|
15
|
-
message
|
|
16
|
-
});
|
|
17
|
-
const fromEntries = (entries) => Object.fromEntries(entries);
|
|
18
|
-
const resultsFor = (names, result) => fromEntries(names.map((name) => Tuple.make(name, result)));
|
|
19
|
-
/**
|
|
20
|
-
* Builds the connector service from its layer and validates only the selected
|
|
21
|
-
* resources. The layer remains open until every check finishes.
|
|
22
|
-
*/
|
|
23
|
-
function check(service, layer, options) {
|
|
24
|
-
if (options.resources.length === 0) return Effect.succeed(fromEntries([]));
|
|
25
|
-
return service.pipe(Effect.flatMap((connector) => Effect.forEach(options.resources, (name) => {
|
|
26
|
-
const resource = connector.resources.find((candidate) => candidate.name === name);
|
|
27
|
-
return (resource ? resource.check.pipe(Effect.match({
|
|
28
|
-
onFailure: (failure) => error(failure.message),
|
|
29
|
-
onSuccess: () => ok
|
|
30
|
-
})) : Effect.succeed(error(`Unknown connector resource: ${name}`))).pipe(Effect.map((result) => Tuple.make(name, result)));
|
|
31
|
-
}).pipe(Effect.map(fromEntries))), Effect.provide(layer), Effect.match({
|
|
32
|
-
onFailure: (failure) => resultsFor(options.resources, error(failure.message)),
|
|
33
|
-
onSuccess: (results) => results
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/connector-app/create-table.ts
|
|
38
|
-
/** Creates the table command for a connector's row schemas. */
|
|
39
|
-
const makeCreateTableCommand = (schemas) => Command.make("create-table", {
|
|
40
|
-
table: Argument.String("table"),
|
|
41
|
-
catalog: Flag.String("catalog"),
|
|
42
|
-
location: Flag.String("location").pipe(Flag.optional),
|
|
43
|
-
resource: Flag.String("resource").pipe(Flag.optional)
|
|
44
|
-
}, ({ table, catalog, location, resource }) => Effect.gen(function* () {
|
|
45
|
-
const namespace = table.split(".");
|
|
46
|
-
const name = namespace.pop();
|
|
47
|
-
if (!name || namespace.length === 0 || namespace.some((part) => part.length === 0)) return yield* new CliError.UserError({
|
|
48
|
-
cause: "Invalid table name",
|
|
49
|
-
userMessage: "Use a table name like my.namespace.table"
|
|
50
|
-
});
|
|
51
|
-
const resourceName = Option.getOrElse(resource, () => name);
|
|
52
|
-
const schema = Object.hasOwn(schemas, resourceName) ? schemas[resourceName] : void 0;
|
|
53
|
-
if (!schema) return yield* new CliError.UserError({
|
|
54
|
-
cause: "Unknown resource",
|
|
55
|
-
userMessage: `Unknown resource: ${resourceName}`
|
|
56
|
-
});
|
|
57
|
-
const uri = yield* wingsUri;
|
|
58
|
-
yield* createTableFromWings(schema, {
|
|
59
|
-
wingsUri: uri.href,
|
|
60
|
-
catalogId: catalog,
|
|
61
|
-
identifier: {
|
|
62
|
-
namespace,
|
|
63
|
-
name
|
|
64
|
-
},
|
|
65
|
-
location: Option.getOrUndefined(location)
|
|
66
|
-
});
|
|
67
|
-
yield* Effect.log(`Created ${table}`);
|
|
68
|
-
})).pipe(Command.withDescription("Create an Iceberg table for a connector resource"));
|
|
69
|
-
//#endregion
|
|
70
|
-
//#region src/connector-app/demo.ts
|
|
71
|
-
/** Reads sizes such as `512KiB`, `128MiB` or `1GiB`. */
|
|
72
|
-
const parseSize = (input) => {
|
|
73
|
-
if (!/^\d+(KiB|MiB|GiB)$/.test(input)) return Option.none();
|
|
74
|
-
return ByteSize.fromString(input).pipe(Option.flatMap(ByteSize.toNumber), Option.filter((bytes) => bytes > 0));
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Creates the command that writes the demo data to Wings, then exits.
|
|
78
|
-
* It reads Wings and table bindings from Config, so provide `RuntimeConfig.layerHosted()`.
|
|
79
|
-
*/
|
|
80
|
-
const makeDemoCommand = (demo) => Command.make("demo", {
|
|
81
|
-
size: Flag.String("size").pipe(Flag.filterMap(parseSize, () => "a size like 512KiB, 128MiB or 1GiB")),
|
|
82
|
-
seed: Flag.Int("seed").pipe(Flag.withDefault(1))
|
|
83
|
-
}, ({ size, seed }) => Effect.gen(function* () {
|
|
84
|
-
const now = yield* DateTime.nowAsDate;
|
|
85
|
-
yield* run(demo, {
|
|
86
|
-
bytes: size,
|
|
87
|
-
seed,
|
|
88
|
-
now
|
|
89
|
-
}).pipe(Effect.provide(layerWingsConfig({ resources: [...demo.setup.tables, ...demo.group.tables] })));
|
|
90
|
-
})).pipe(Command.withDescription("Write demo data for the bound tables, then exit"));
|
|
91
|
-
//#endregion
|
|
92
|
-
//#region src/connector-app/start.ts
|
|
93
|
-
const start = (app, options) => Effect.gen(function* () {
|
|
94
|
-
const initialCutoff = options.initialCutoff ?? (yield* DateTime.now.pipe(Effect.map(DateTime.formatIso)));
|
|
95
|
-
const routes = app.webhooks?.map((route) => route.path) ?? [];
|
|
96
|
-
yield* Effect.logInfo("webhook server ready").pipe(Effect.annotateLogs({
|
|
97
|
-
port: options.port,
|
|
98
|
-
routes
|
|
99
|
-
}));
|
|
100
|
-
return yield* run$1(app, {
|
|
101
|
-
initialCutoff,
|
|
102
|
-
webhook: {
|
|
103
|
-
routes: app.webhooks ?? [],
|
|
104
|
-
healthPath: options.healthPath ?? "/health",
|
|
105
|
-
metricsPath: options.metricsPath,
|
|
106
|
-
statusPath: options.statusPath,
|
|
107
|
-
disableHttpLogger: options.disableHttpLogger ?? true
|
|
108
|
-
}
|
|
109
|
-
}).pipe(Effect.provide(NodeHttpServer.layer(createServer, { port: options.port })));
|
|
110
|
-
});
|
|
111
|
-
//#endregion
|
|
112
|
-
//#region src/connector-app/index.ts
|
|
113
|
-
var connector_app_exports = /* @__PURE__ */ __exportAll({
|
|
114
|
-
check: () => check,
|
|
115
|
-
makeCreateTableCommand: () => makeCreateTableCommand,
|
|
116
|
-
makeDemoCommand: () => makeDemoCommand,
|
|
117
|
-
parseSize: () => parseSize,
|
|
118
|
-
start: () => start
|
|
119
|
-
});
|
|
120
|
-
//#endregion
|
|
121
|
-
export { makeCreateTableCommand as a, parseSize as i, start as n, check as o, makeDemoCommand as r, connector_app_exports as t };
|
|
122
|
-
|
|
123
|
-
//# sourceMappingURL=connector-app-C-L4QqG6.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"connector-app-C-L4QqG6.mjs","names":["runIngestion"],"sources":["../src/connector-app/check.ts","../src/connector-app/create-table.ts","../src/connector-app/demo.ts","../src/connector-app/start.ts","../src/connector-app/index.ts"],"sourcesContent":["import { Context, Effect, Layer, Tuple } from \"effect\";\n\nimport type { ConnectorDefinition, ResourceDefinition, ResourceName } from \"../core/types\";\n\nexport type ResourceCheckResult =\n | { readonly _tag: \"ok\" }\n | { readonly _tag: \"error\"; readonly message: string };\n\nexport type CheckOptions<Name extends string> = {\n readonly resources: ReadonlyArray<Name>;\n};\n\ntype ResourceCheckResults<Name extends PropertyKey> = {\n readonly [Key in Name]: ResourceCheckResult;\n};\n\nexport type CheckResult<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Name extends ResourceName<Resources> = ResourceName<Resources>,\n> = ResourceCheckResults<Name>;\n\ntype CheckFailure = { readonly message: string };\n\nconst ok: ResourceCheckResult = { _tag: \"ok\" };\n\nconst error = (message: string): ResourceCheckResult => ({ _tag: \"error\", message });\n\nconst fromEntries = <Name extends string>(\n entries: ReadonlyArray<readonly [Name, ResourceCheckResult]>,\n): ResourceCheckResults<Name> => Object.fromEntries(entries) as ResourceCheckResults<Name>;\n\nconst resultsFor = <Name extends string>(names: ReadonlyArray<Name>, result: ResourceCheckResult) =>\n fromEntries(names.map((name) => Tuple.make(name, result)));\n\n/**\n * Builds the connector service from its layer and validates only the selected\n * resources. The layer remains open until every check finishes.\n */\nexport function check<\n Identifier,\n const Resources extends ReadonlyArray<ResourceDefinition>,\n E extends CheckFailure,\n R,\n const Name extends ResourceName<Resources>,\n>(\n service: Context.Key<Identifier, ConnectorDefinition<Resources>>,\n layer: Layer.Layer<Identifier, E, R>,\n options: CheckOptions<Name>,\n): Effect.Effect<CheckResult<Resources, Name>, never, R> {\n if (options.resources.length === 0) {\n return Effect.succeed(fromEntries<Name>([]));\n }\n\n return service.pipe(\n Effect.flatMap((connector) =>\n Effect.forEach(options.resources, (name) => {\n const resource = connector.resources.find((candidate) => candidate.name === name);\n const result = resource\n ? resource.check.pipe(\n Effect.match({\n onFailure: (failure) => error(failure.message),\n onSuccess: () => ok,\n }),\n )\n : Effect.succeed(error(`Unknown connector resource: ${name}`));\n\n return result.pipe(Effect.map((result) => Tuple.make(name, result)));\n }).pipe(Effect.map(fromEntries)),\n ),\n Effect.provide(layer),\n Effect.match({\n onFailure: (failure) => resultsFor(options.resources, error(failure.message)),\n onSuccess: (results) => results,\n }),\n );\n}\n","import { Effect, Option, type Schema } from \"effect\";\nimport { Argument, CliError, Command, Flag } from \"effect/cli\";\n\nimport { createTableFromWings } from \"../catalog/table\";\nimport { wingsUri } from \"../runtime-config/config\";\n\n/** Creates the table command for a connector's row schemas. */\nexport const makeCreateTableCommand = (schemas: Readonly<Record<string, Schema.Top>>) =>\n Command.make(\n \"create-table\",\n {\n table: Argument.String(\"table\"),\n catalog: Flag.String(\"catalog\"),\n location: Flag.String(\"location\").pipe(Flag.optional),\n resource: Flag.String(\"resource\").pipe(Flag.optional),\n },\n ({ table, catalog, location, resource }) =>\n Effect.gen(function* () {\n const namespace = table.split(\".\");\n const name = namespace.pop();\n\n if (!name || namespace.length === 0 || namespace.some((part) => part.length === 0)) {\n return yield* new CliError.UserError({\n cause: \"Invalid table name\",\n userMessage: \"Use a table name like my.namespace.table\",\n });\n }\n\n const resourceName = Option.getOrElse(resource, () => name);\n const schema = Object.hasOwn(schemas, resourceName) ? schemas[resourceName] : undefined;\n if (!schema) {\n return yield* new CliError.UserError({\n cause: \"Unknown resource\",\n userMessage: `Unknown resource: ${resourceName}`,\n });\n }\n\n const uri = yield* wingsUri;\n yield* createTableFromWings(schema, {\n wingsUri: uri.href,\n catalogId: catalog,\n identifier: { namespace, name },\n location: Option.getOrUndefined(location),\n });\n yield* Effect.log(`Created ${table}`);\n }),\n ).pipe(Command.withDescription(\"Create an Iceberg table for a connector resource\"));\n","import { ByteSize, DateTime, Effect, Option } from \"effect\";\nimport { Command, Flag } from \"effect/cli\";\n\nimport { run, type DemoDefinition } from \"../demo\";\nimport { layerWingsConfig } from \"../ingestor/wings\";\n\n/** Reads sizes such as `512KiB`, `128MiB` or `1GiB`. */\nexport const parseSize = (input: string): Option.Option<number> => {\n if (!/^\\d+(KiB|MiB|GiB)$/.test(input)) return Option.none();\n\n return ByteSize.fromString(input).pipe(\n Option.flatMap(ByteSize.toNumber),\n Option.filter((bytes) => bytes > 0),\n );\n};\n\n/**\n * Creates the command that writes the demo data to Wings, then exits.\n * It reads Wings and table bindings from Config, so provide `RuntimeConfig.layerHosted()`.\n */\nexport const makeDemoCommand = (demo: DemoDefinition) =>\n Command.make(\n \"demo\",\n {\n size: Flag.String(\"size\").pipe(\n Flag.filterMap(parseSize, () => \"a size like 512KiB, 128MiB or 1GiB\"),\n ),\n seed: Flag.Int(\"seed\").pipe(Flag.withDefault(1)),\n },\n ({ size, seed }) =>\n Effect.gen(function* () {\n const now = yield* DateTime.nowAsDate;\n\n yield* run(demo, { bytes: size, seed, now }).pipe(\n Effect.provide(\n layerWingsConfig({\n resources: [...demo.setup.tables, ...demo.group.tables],\n }),\n ),\n );\n }),\n ).pipe(Command.withDescription(\"Write demo data for the bound tables, then exit\"));\n","import { NodeHttpServer } from \"@effect/platform-node\";\nimport { DateTime, Effect } from \"effect\";\nimport { HttpRouter } from \"effect/http\";\nimport { createServer } from \"node:http\";\n\nimport type { ConnectorDefinition } from \"../core\";\nimport type { Cursor } from \"../core/types\";\n\nimport { run as runIngestion } from \"../ingestion\";\n\nexport type App = ConnectorDefinition;\n\nexport type StartOptions = {\n readonly port: number;\n readonly initialCutoff?: Cursor.Value;\n readonly healthPath?: HttpRouter.PathInput;\n readonly metricsPath?: HttpRouter.PathInput | false;\n readonly statusPath?: HttpRouter.PathInput | false;\n readonly disableHttpLogger?: boolean;\n};\n\nexport const start = (app: App, options: StartOptions) =>\n Effect.gen(function* () {\n const initialCutoff =\n options.initialCutoff ?? (yield* DateTime.now.pipe(Effect.map(DateTime.formatIso)));\n const routes = app.webhooks?.map((route) => route.path) ?? [];\n\n yield* Effect.logInfo(\"webhook server ready\").pipe(\n Effect.annotateLogs({ port: options.port, routes }),\n );\n\n return yield* runIngestion(app, {\n initialCutoff,\n webhook: {\n routes: app.webhooks ?? [],\n healthPath: options.healthPath ?? \"/health\",\n metricsPath: options.metricsPath,\n statusPath: options.statusPath,\n disableHttpLogger: options.disableHttpLogger ?? true,\n },\n }).pipe(Effect.provide(NodeHttpServer.layer(createServer, { port: options.port })));\n });\n","export { check, type CheckOptions, type CheckResult, type ResourceCheckResult } from \"./check\";\nexport { makeCreateTableCommand } from \"./create-table\";\nexport { makeDemoCommand, parseSize } from \"./demo\";\nexport { start, type App, type StartOptions } from \"./start\";\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants-D-GQzpHZ.mjs","names":[],"sources":["../src/runtime-config/constants.ts"],"sourcesContent":["import { HashSet } from \"effect\";\n\n/** Shared runtime keys owned by Airfoil rather than an individual connector. */\nexport const PlatformRuntimeKey = {\n configPath: \"AIRFOIL_CONFIG_PATH\",\n httpPort: \"AIRFOIL_HTTP_PORT\",\n tableBindings: \"AIRFOIL_TABLE_BINDINGS\",\n connectorInstanceId: \"AIRFOIL_CONNECTOR_INSTANCE_ID\",\n stateTable: \"AIRFOIL_STATE_TABLE\",\n postgresConnectionString: \"POSTGRES_CONNECTION_STRING\",\n wingsUri: \"WINGS_URI\",\n catalog: \"AIRFOIL_CATALOG\",\n otelEnabled: \"OTEL_ENABLED\",\n otelExporterOtlpEndpoint: \"OTEL_EXPORTER_OTLP_ENDPOINT\",\n otelExporterOtlpHeaders: \"OTEL_EXPORTER_OTLP_HEADERS\",\n otelServiceName: \"OTEL_SERVICE_NAME\",\n otelServiceVersion: \"OTEL_SERVICE_VERSION\",\n otelResourceAttributes: \"OTEL_RESOURCE_ATTRIBUTES\",\n} as const;\n\n/** Defaults for optional platform-owned runtime configuration. */\nexport const PlatformRuntimeDefault = {\n httpPort: 8080,\n stateTable: \"_airfoil_connectors_state\",\n} as const;\n\nconst platformRuntimeKeys = HashSet.fromIterable(Object.values(PlatformRuntimeKey));\n\n/** @internal */\nexport const isPlatformRuntimeKey = (key: string): boolean => HashSet.has(platformRuntimeKeys, key);\n"],"mappings":";;;AAGA,MAAa,qBAAqB;CAChC,YAAY;CACZ,UAAU;CACV,eAAe;CACf,qBAAqB;CACrB,YAAY;CACZ,0BAA0B;CAC1B,UAAU;CACV,SAAS;CACT,aAAa;CACb,0BAA0B;CAC1B,yBAAyB;CACzB,iBAAiB;CACjB,oBAAoB;CACpB,wBAAwB;AAC1B;;AAGA,MAAa,yBAAyB;CACpC,UAAU;CACV,YAAY;AACd;AAEA,MAAM,sBAAsB,QAAQ,aAAa,OAAO,OAAO,kBAAkB,CAAC;;AAGlF,MAAa,wBAAwB,QAAyB,QAAQ,IAAI,qBAAqB,GAAG"}
|