@useairfoil/connector-kit 0.6.0 → 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 +38 -42
- package/README.md +111 -444
- package/dist/annotations-vzSzXtr-.d.mts +34 -0
- package/dist/arrow-flight-DDQ0ktx9.mjs +251 -0
- package/dist/arrow-flight-DDQ0ktx9.mjs.map +1 -0
- package/dist/auth/index.d.mts +2 -0
- package/dist/{auth-DvU_CZ7C.js → auth/index.mjs} +6 -8
- package/dist/auth/index.mjs.map +1 -0
- package/dist/connector-app/index.d.mts +2 -0
- package/dist/connector-app/index.mjs +2 -0
- package/dist/connector-app-C6_dZnhE.mjs +256 -0
- package/dist/connector-app-C6_dZnhE.mjs.map +1 -0
- package/dist/{constants-D113Ut1x.js → constants-v3UX-hf6.mjs} +5 -5
- package/dist/constants-v3UX-hf6.mjs.map +1 -0
- package/dist/demo/index.d.mts +2 -0
- package/dist/demo/index.mjs +2 -0
- package/dist/demo-DqTJ7TWI.mjs +204 -0
- package/dist/demo-DqTJ7TWI.mjs.map +1 -0
- package/dist/errors/index.d.mts +2 -0
- package/dist/errors/index.mjs +2 -0
- package/dist/{errors-DHWylqRZ.js → errors-pT0YVJU5.mjs} +2 -3
- package/dist/errors-pT0YVJU5.mjs.map +1 -0
- package/dist/iceberg/index.d.mts +3 -0
- package/dist/iceberg/index.mjs +3 -0
- package/dist/iceberg-Cj2PwlNm.mjs +30 -0
- package/dist/iceberg-Cj2PwlNm.mjs.map +1 -0
- package/dist/index-BTAbexBp.d.mts +55 -0
- package/dist/{index-DXa26Ry5.d.ts → index-BX8G7d41.d.mts} +1 -2
- package/dist/{index-VpHaZXZw.d.ts → index-Byr8NTdG.d.mts} +11 -13
- package/dist/index-CV6V6fyx.d.mts +79 -0
- package/dist/{index-C5Ma7V7t.d.ts → index-CZKx2Yce.d.mts} +72 -37
- package/dist/{index-DYpEMJf-.d.ts → index-CgvFomO6.d.mts} +6 -7
- package/dist/{index-bfjH7Yb0.d.ts → index-DeTwIUKl.d.mts} +9 -10
- package/dist/{index-DKoRwyFn.d.ts → index-DkVNO6ks.d.mts} +2 -5
- package/dist/{index-BKw7jA2a.d.ts → index-dHS59EZl.d.mts} +10 -8
- package/dist/{index-CR9HJDdr.d.ts → index-eFuoMZZ3.d.mts} +18 -20
- package/dist/{index-ZSuiZX5H.d.ts → index-fHIJJoRe.d.mts} +25 -30
- package/dist/index-uFa2wwYo.d.mts +27 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +45 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ingestion/index.d.mts +2 -0
- package/dist/ingestion/index.mjs +2 -0
- package/dist/ingestion-DoCUwm5N.mjs +275 -0
- package/dist/ingestion-DoCUwm5N.mjs.map +1 -0
- package/dist/ingestor/index.d.mts +3 -0
- package/dist/ingestor/index.mjs +4 -0
- package/dist/ingestor-DjCEzBzZ.mjs +26 -0
- package/dist/ingestor-DjCEzBzZ.mjs.map +1 -0
- package/dist/instrumented-Bq1WX2L5.mjs +29 -0
- package/dist/instrumented-Bq1WX2L5.mjs.map +1 -0
- package/dist/manifest/index.d.mts +2 -0
- package/dist/manifest/{index.js → index.mjs} +2 -4
- package/dist/{manifest-P7kBnch9.js → manifest-CABeIhAG.mjs} +14 -19
- package/dist/manifest-CABeIhAG.mjs.map +1 -0
- package/dist/{telemetry-j_nxwYfh.js → metrics-C901IIYZ.mjs} +172 -26
- package/dist/metrics-C901IIYZ.mjs.map +1 -0
- package/dist/{metrics-C4V1pm6v.d.ts → metrics-DoJ0WeKu.d.mts} +18 -13
- package/dist/metrics.d.mts +2 -0
- package/dist/metrics.mjs +2 -0
- package/dist/{chunk-Bp6m_JJh.js → rolldown-runtime-D7D4PA-g.mjs} +4 -4
- package/dist/runtime-config/index.d.mts +2 -0
- package/dist/runtime-config/index.mjs +3 -0
- package/dist/runtime-config-Bn8Fivt1.mjs +100 -0
- package/dist/runtime-config-Bn8Fivt1.mjs.map +1 -0
- package/dist/server-D1TTN0_D.mjs +144 -0
- package/dist/server-D1TTN0_D.mjs.map +1 -0
- package/dist/service-BRoy_EId.d.mts +27 -0
- package/dist/service-jWxxfEht.mjs +7 -0
- package/dist/service-jWxxfEht.mjs.map +1 -0
- package/dist/state-store/index.d.mts +2 -0
- package/dist/state-store/index.mjs +2 -0
- package/dist/{state-store-BdYulQOY.js → state-store-DhSrKVEH.mjs} +15 -22
- package/dist/state-store-DhSrKVEH.mjs.map +1 -0
- package/dist/status/{index.d.ts → index.d.mts} +1 -4
- package/dist/status/{index.js → index.mjs} +2 -6
- package/dist/{status-BTTd7AUR.js → status-4h6Oxh-O.mjs} +14 -16
- package/dist/status-4h6Oxh-O.mjs.map +1 -0
- package/dist/table-CxM09xf7.mjs +307 -0
- package/dist/table-CxM09xf7.mjs.map +1 -0
- package/dist/types-NtSv6gwl.d.mts +220 -0
- package/dist/webhook/index.d.mts +3 -0
- package/dist/webhook/index.mjs +12 -0
- package/dist/webhook/index.mjs.map +1 -0
- package/package.json +72 -58
- package/src/catalog/retry.ts +25 -0
- package/src/catalog/table.ts +237 -0
- package/src/connector-app/create-table.ts +47 -0
- package/src/connector-app/demo.ts +47 -0
- package/src/connector-app/index.ts +3 -0
- package/src/connector-app/setup.ts +69 -0
- package/src/connector-app/start.ts +1 -1
- package/src/core/builder.ts +15 -23
- package/src/core/index.ts +11 -1
- package/src/core/types.ts +114 -39
- package/src/demo/definition.ts +68 -0
- package/src/demo/generate.ts +167 -0
- package/src/demo/index.ts +3 -0
- package/src/demo/run.ts +172 -0
- package/src/iceberg/annotations.ts +49 -0
- package/src/iceberg/error.ts +9 -0
- package/src/iceberg/index.ts +3 -0
- package/src/iceberg/table.ts +461 -0
- package/src/index.ts +3 -1
- package/src/ingestion/engine.ts +240 -60
- package/src/ingestor/arrow-flight.ts +141 -0
- package/src/ingestor/arrow.ts +248 -0
- package/src/ingestor/console.ts +13 -0
- package/src/ingestor/index.ts +13 -0
- package/src/ingestor/instrumented.ts +61 -0
- package/src/ingestor/service.ts +30 -0
- package/src/manifest/config.ts +3 -3
- package/src/metrics.ts +66 -20
- package/src/runtime-config/config.ts +11 -8
- package/src/runtime-config/constants.ts +3 -2
- package/src/runtime-config/index.ts +2 -7
- package/src/runtime-config/layer.ts +1 -1
- package/src/runtime-config/table-bindings.ts +50 -136
- package/src/state-store/layer-key-value-store.ts +8 -3
- package/src/state-store/layer-memory.ts +1 -1
- package/src/state-store/layer-sql.ts +4 -4
- package/src/state-store/schema.ts +2 -2
- package/src/state-store/state.ts +14 -2
- package/src/status/route.ts +2 -2
- package/src/telemetry.ts +20 -28
- package/src/webhook/server.ts +39 -40
- package/dist/auth/index.d.ts +0 -2
- package/dist/auth/index.js +0 -3
- package/dist/auth-DvU_CZ7C.js.map +0 -1
- package/dist/connector-app/index.d.ts +0 -14
- package/dist/connector-app/index.js +0 -12
- package/dist/connector-app-BljHd0Pe.js +0 -64
- package/dist/connector-app-BljHd0Pe.js.map +0 -1
- package/dist/constants-D113Ut1x.js.map +0 -1
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -3
- package/dist/errors-DHWylqRZ.js.map +0 -1
- package/dist/index-BA8ASHKr.d.ts +0 -31
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -60
- package/dist/index.js.map +0 -1
- package/dist/ingestion/index.d.ts +0 -6
- package/dist/ingestion/index.js +0 -11
- package/dist/ingestion-QBGKrFr9.js +0 -200
- package/dist/ingestion-QBGKrFr9.js.map +0 -1
- package/dist/manifest/index.d.ts +0 -2
- package/dist/manifest-P7kBnch9.js.map +0 -1
- package/dist/metrics-inkCAxKq.js +0 -136
- package/dist/metrics-inkCAxKq.js.map +0 -1
- package/dist/metrics.d.ts +0 -4
- package/dist/metrics.js +0 -5
- package/dist/publisher/index.d.ts +0 -6
- package/dist/publisher/index.js +0 -8
- package/dist/publisher-BdWJOfer.js +0 -138
- package/dist/publisher-BdWJOfer.js.map +0 -1
- package/dist/runtime-config/index.d.ts +0 -4
- package/dist/runtime-config/index.js +0 -5
- package/dist/runtime-config-IbyCVpei.js +0 -60
- package/dist/runtime-config-IbyCVpei.js.map +0 -1
- package/dist/server-D-eLX3yN.js +0 -183
- package/dist/server-D-eLX3yN.js.map +0 -1
- package/dist/service-CUjGhe-7.d.ts +0 -32
- package/dist/service-DiP-XETo.js +0 -8
- package/dist/service-DiP-XETo.js.map +0 -1
- package/dist/state-store/index.d.ts +0 -3
- package/dist/state-store/index.js +0 -5
- package/dist/state-store-BdYulQOY.js.map +0 -1
- package/dist/status-BTTd7AUR.js.map +0 -1
- package/dist/table-bindings-Dp1DhTeS.js +0 -106
- package/dist/table-bindings-Dp1DhTeS.js.map +0 -1
- package/dist/telemetry-j_nxwYfh.js.map +0 -1
- package/dist/types-jVD5Ys43.d.ts +0 -174
- package/dist/webhook/index.d.ts +0 -7
- package/dist/webhook/index.js +0 -9
- package/dist/webhook-MhFcLW-o.js +0 -13
- package/dist/webhook-MhFcLW-o.js.map +0 -1
- package/src/publisher/console.ts +0 -18
- package/src/publisher/index.ts +0 -7
- package/src/publisher/instrumented.ts +0 -75
- package/src/publisher/service.ts +0 -37
- package/src/publisher/wings.ts +0 -204
|
@@ -1,67 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Config, Effect, Schema } from "effect";
|
|
1
|
+
import type { TableIdentifier } from "iceberg-js";
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import { Config, Effect, Schema } from "effect";
|
|
5
4
|
|
|
6
5
|
import { PlatformRuntimeKey } from "./constants";
|
|
7
6
|
import { RuntimeConfigError } from "./error";
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
| string
|
|
12
|
-
| {
|
|
13
|
-
readonly name: string;
|
|
14
|
-
readonly partition?: Wings.PartitionValue.Json;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/** Validated table binding ready for the Wings publisher. */
|
|
18
|
-
export type ResolvedTableBinding = {
|
|
19
|
-
readonly name: string;
|
|
20
|
-
readonly partitionValue?: Wings.PartitionValue.PartitionValue;
|
|
8
|
+
type BindingSource = {
|
|
9
|
+
readonly resources: ReadonlyArray<{ readonly name: string }>;
|
|
21
10
|
};
|
|
22
11
|
|
|
23
|
-
/**
|
|
24
|
-
export type
|
|
25
|
-
|
|
12
|
+
/** An Iceberg table and its storage location for each connector resource. */
|
|
13
|
+
export type TableBinding = TableIdentifier & { readonly location?: string | undefined };
|
|
14
|
+
|
|
15
|
+
export type ResolvedTableBindings<Source extends BindingSource> = Readonly<
|
|
16
|
+
Partial<Record<Source["resources"][number]["name"], TableBinding>>
|
|
26
17
|
>;
|
|
27
18
|
|
|
28
|
-
const
|
|
19
|
+
const TableIdentifierSchema = Schema.Struct({
|
|
20
|
+
namespace: Schema.NonEmptyArray(Schema.NonEmptyString),
|
|
21
|
+
name: Schema.NonEmptyString,
|
|
22
|
+
location: Schema.optional(Schema.NonEmptyString),
|
|
23
|
+
});
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let depth = 0;
|
|
34
|
-
let expectKey = false;
|
|
35
|
-
for (let index = 0; index < json.length; index += 1) {
|
|
36
|
-
const char = json[index];
|
|
37
|
-
if (char === '"') {
|
|
38
|
-
const start = index;
|
|
39
|
-
for (index += 1; index < json.length; index += 1) {
|
|
40
|
-
if (json[index] === "\\") {
|
|
41
|
-
index += 1;
|
|
42
|
-
} else if (json[index] === '"') {
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (depth === 1 && expectKey) {
|
|
47
|
-
const key = JSON.parse(json.slice(start, index + 1)) as string;
|
|
48
|
-
if (keys.has(key)) return key;
|
|
49
|
-
keys.add(key);
|
|
50
|
-
expectKey = false;
|
|
51
|
-
}
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (char === "{") {
|
|
55
|
-
depth += 1;
|
|
56
|
-
if (depth === 1) expectKey = true;
|
|
57
|
-
} else if (char === "}") {
|
|
58
|
-
depth -= 1;
|
|
59
|
-
} else if (char === "," && depth === 1) {
|
|
60
|
-
expectKey = true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
};
|
|
25
|
+
const RawBindingsSchema = Schema.fromJsonString(
|
|
26
|
+
Schema.Record(Schema.String, TableIdentifierSchema),
|
|
27
|
+
);
|
|
65
28
|
|
|
66
29
|
const asError = (cause: unknown): Error =>
|
|
67
30
|
cause instanceof Error ? cause : new Error(String(cause));
|
|
@@ -69,64 +32,12 @@ const asError = (cause: unknown): Error =>
|
|
|
69
32
|
const fail = (code: "TABLE_BINDINGS_INVALID" | "TABLE_BINDINGS_MISMATCH", message: string) =>
|
|
70
33
|
new RuntimeConfigError({ code, message, cause: new Error(message) });
|
|
71
34
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const keys = Object.keys(value);
|
|
77
|
-
return (
|
|
78
|
-
keys.length === allowed.filter((key) => key in value).length &&
|
|
79
|
-
keys.every((key) => allowed.includes(key))
|
|
80
|
-
);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const nonEmptyName = (value: unknown): string => {
|
|
84
|
-
if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
|
|
85
|
-
throw fail("TABLE_BINDINGS_INVALID", "Table binding names must be non-empty strings");
|
|
86
|
-
}
|
|
87
|
-
return value;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const resolveBinding = (input: unknown): Effect.Effect<ResolvedTableBinding, RuntimeConfigError> =>
|
|
91
|
-
Effect.gen(function* () {
|
|
92
|
-
const binding = yield* Effect.try({
|
|
93
|
-
try: () => {
|
|
94
|
-
if (typeof input === "string") return { name: nonEmptyName(input) } as const;
|
|
95
|
-
if (!isRecord(input) || !hasExactKeys(input, ["name", "partition"])) {
|
|
96
|
-
throw fail(
|
|
97
|
-
"TABLE_BINDINGS_INVALID",
|
|
98
|
-
"Table bindings must be a name string or an object containing name and optional partition",
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return { name: nonEmptyName(input.name), partition: input.partition } as const;
|
|
102
|
-
},
|
|
103
|
-
catch: (cause) =>
|
|
104
|
-
cause instanceof RuntimeConfigError
|
|
105
|
-
? cause
|
|
106
|
-
: fail("TABLE_BINDINGS_INVALID", "Hosted table binding is invalid"),
|
|
107
|
-
});
|
|
108
|
-
if (!("partition" in binding) || binding.partition === undefined) {
|
|
109
|
-
return { name: binding.name };
|
|
110
|
-
}
|
|
111
|
-
const partitionValue = yield* Wings.PartitionValue.decodeJson(binding.partition).pipe(
|
|
112
|
-
Effect.mapError(
|
|
113
|
-
(cause) =>
|
|
114
|
-
new RuntimeConfigError({
|
|
115
|
-
code: "TABLE_BINDINGS_INVALID",
|
|
116
|
-
message: "Hosted table binding has an invalid partition value",
|
|
117
|
-
cause,
|
|
118
|
-
}),
|
|
119
|
-
),
|
|
120
|
-
);
|
|
121
|
-
return { name: binding.name, partitionValue };
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
/** Loads and validates the complete hosted Wings table map for a connector. */
|
|
125
|
-
export const loadTableBindings = <const Connector extends ConnectorDefinition>(
|
|
126
|
-
connector: Connector,
|
|
127
|
-
): Effect.Effect<ResolvedTableBindings<Connector>, RuntimeConfigError> =>
|
|
35
|
+
/** Reads hosted table bindings and checks they name resources of the connector. */
|
|
36
|
+
export const loadTableBindings = <const Source extends BindingSource>(
|
|
37
|
+
connector: Source,
|
|
38
|
+
): Effect.Effect<ResolvedTableBindings<Source>, RuntimeConfigError> =>
|
|
128
39
|
Effect.gen(function* () {
|
|
129
|
-
const raw = yield* Config.
|
|
40
|
+
const raw = yield* Config.NonEmptyString(PlatformRuntimeKey.tableBindings).pipe(
|
|
130
41
|
Effect.mapError(
|
|
131
42
|
(cause) =>
|
|
132
43
|
new RuntimeConfigError({
|
|
@@ -136,45 +47,48 @@ export const loadTableBindings = <const Connector extends ConnectorDefinition>(
|
|
|
136
47
|
}),
|
|
137
48
|
),
|
|
138
49
|
);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
code: "TABLE_BINDINGS_INVALID",
|
|
144
|
-
message: "Hosted table bindings are not valid JSON",
|
|
145
|
-
cause: asError(cause),
|
|
146
|
-
}),
|
|
147
|
-
});
|
|
148
|
-
if (duplicate !== undefined) {
|
|
149
|
-
return yield* Effect.fail(
|
|
150
|
-
fail("TABLE_BINDINGS_INVALID", "Hosted table bindings contain a duplicate resource"),
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
const decoded = yield* Schema.decodeUnknownEffect(RawBindingsSchema)(raw).pipe(
|
|
50
|
+
|
|
51
|
+
const decoded = yield* Schema.decodeUnknownEffect(RawBindingsSchema)(raw, {
|
|
52
|
+
onExcessProperty: "error",
|
|
53
|
+
}).pipe(
|
|
154
54
|
Effect.mapError(
|
|
155
55
|
(cause) =>
|
|
156
56
|
new RuntimeConfigError({
|
|
157
57
|
code: "TABLE_BINDINGS_INVALID",
|
|
158
|
-
message: "Hosted table bindings are
|
|
58
|
+
message: "Hosted table bindings are invalid",
|
|
159
59
|
cause: asError(cause),
|
|
160
60
|
}),
|
|
161
61
|
),
|
|
162
62
|
);
|
|
163
|
-
|
|
164
|
-
const expected = new Set(connector.resources.map((resource) => resource.name));
|
|
63
|
+
|
|
165
64
|
const actual = Object.keys(decoded);
|
|
166
|
-
|
|
65
|
+
if (actual.length === 0) {
|
|
66
|
+
return yield* Effect.fail(
|
|
67
|
+
fail("TABLE_BINDINGS_INVALID", "Hosted table bindings name no resources"),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Bindings come from the platform, resources from the connector. An unknown name
|
|
72
|
+
// means one of them is out of date, and we could write to the wrong table.
|
|
73
|
+
const expected = new Set(connector.resources.map((resource) => resource.name));
|
|
167
74
|
const unknown = actual.filter((name) => !expected.has(name));
|
|
168
|
-
if (
|
|
75
|
+
if (unknown.length > 0) {
|
|
169
76
|
return yield* Effect.fail(
|
|
170
77
|
fail(
|
|
171
78
|
"TABLE_BINDINGS_MISMATCH",
|
|
172
|
-
`Hosted table bindings
|
|
79
|
+
`Hosted table bindings name resources the connector doesn't have (${unknown.length})`,
|
|
173
80
|
),
|
|
174
81
|
);
|
|
175
82
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
83
|
+
|
|
84
|
+
return Object.fromEntries(
|
|
85
|
+
Object.entries(decoded).map(([resource, identifier]) => [
|
|
86
|
+
resource,
|
|
87
|
+
{
|
|
88
|
+
namespace: Array.from(identifier.namespace),
|
|
89
|
+
name: identifier.name,
|
|
90
|
+
...(identifier.location === undefined ? {} : { location: identifier.location }),
|
|
91
|
+
},
|
|
92
|
+
]),
|
|
93
|
+
) as ResolvedTableBindings<Source>;
|
|
180
94
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DateTime, Effect, Layer, Option, Schema } from "effect";
|
|
2
|
-
import { KeyValueStore } from "effect/
|
|
2
|
+
import { KeyValueStore } from "effect/persistence";
|
|
3
3
|
|
|
4
4
|
import { ConnectorError } from "../errors";
|
|
5
5
|
import { resourceComponentKey, resourceErrorKey } from "./keys";
|
|
@@ -33,8 +33,13 @@ const resourceErrorContext = (
|
|
|
33
33
|
switch (operation) {
|
|
34
34
|
case "fetch":
|
|
35
35
|
return { source, operation, code: "fetch_failed", message: `${sourceName} fetch failed` };
|
|
36
|
-
case "
|
|
37
|
-
return {
|
|
36
|
+
case "ingest":
|
|
37
|
+
return {
|
|
38
|
+
source,
|
|
39
|
+
operation,
|
|
40
|
+
code: "ingest_failed",
|
|
41
|
+
message: `${sourceName} ingestion failed`,
|
|
42
|
+
};
|
|
38
43
|
case "checkpoint":
|
|
39
44
|
return {
|
|
40
45
|
source,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Config, Effect, Layer } from "effect";
|
|
2
|
-
import { KeyValueStore } from "effect/
|
|
3
|
-
import { SqlClient } from "effect/
|
|
2
|
+
import { KeyValueStore } from "effect/persistence";
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
|
|
5
5
|
import type { StateStore } from "./service";
|
|
6
6
|
|
|
@@ -32,8 +32,8 @@ const scopedKeyValueStore = (connectorInstanceId: string) =>
|
|
|
32
32
|
export const layerSql = (): Layer.Layer<StateStore, Config.ConfigError, SqlClient.SqlClient> =>
|
|
33
33
|
Layer.unwrap(
|
|
34
34
|
Config.all({
|
|
35
|
-
connectorInstanceId: Config.
|
|
36
|
-
table: Config.
|
|
35
|
+
connectorInstanceId: Config.NonEmptyString(PlatformRuntimeKey.connectorInstanceId),
|
|
36
|
+
table: Config.NonEmptyString(PlatformRuntimeKey.stateTable).pipe(
|
|
37
37
|
Config.withDefault(PlatformRuntimeDefault.stateTable),
|
|
38
38
|
),
|
|
39
39
|
}).pipe(
|
|
@@ -18,11 +18,11 @@ export const ChangesStateSchema = Schema.Struct({
|
|
|
18
18
|
|
|
19
19
|
export const StateSourceSchema = Schema.Literals(["backfill", "changes"]);
|
|
20
20
|
|
|
21
|
-
export const StateOperationSchema = Schema.Literals(["fetch", "
|
|
21
|
+
export const StateOperationSchema = Schema.Literals(["fetch", "ingest", "checkpoint"]);
|
|
22
22
|
|
|
23
23
|
export const StateErrorCodeSchema = Schema.Literals([
|
|
24
24
|
"fetch_failed",
|
|
25
|
-
"
|
|
25
|
+
"ingest_failed",
|
|
26
26
|
"checkpoint_failed",
|
|
27
27
|
]);
|
|
28
28
|
|
package/src/state-store/state.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ConnectorDefinition,
|
|
3
|
+
Cursor,
|
|
4
|
+
ResourceDefinition,
|
|
5
|
+
ResourceState,
|
|
6
|
+
SyncState,
|
|
7
|
+
} from "../core/types";
|
|
2
8
|
|
|
3
9
|
/** Normalizes a cursor value to its persisted/wire representation (never a Date). */
|
|
4
10
|
export const normalizeCursor = (value: Cursor.Value): string | number =>
|
|
5
11
|
value instanceof Date ? value.toISOString() : value;
|
|
6
12
|
|
|
13
|
+
/** Whether a resource gets changes, from its own `changes` or the connector's feed. */
|
|
14
|
+
export const hasChanges = (connector: ConnectorDefinition, resource: ResourceDefinition) =>
|
|
15
|
+
resource.changes !== undefined ||
|
|
16
|
+
(connector.changes?.resources.some((member) => member.name === resource.name) ?? false);
|
|
17
|
+
|
|
7
18
|
/** Derives connector-facing sync state from durable state and resource capabilities. */
|
|
8
19
|
export const deriveSyncState = (
|
|
20
|
+
connector: ConnectorDefinition,
|
|
9
21
|
resource: ResourceDefinition,
|
|
10
22
|
state: ResourceState | undefined,
|
|
11
23
|
): SyncState => {
|
|
12
24
|
if (state?.lastError) return "error";
|
|
13
|
-
if (!resource.backfill && !resource
|
|
25
|
+
if (!resource.backfill && !hasChanges(connector, resource)) return "live";
|
|
14
26
|
if (!state) return "pending";
|
|
15
27
|
if (resource.backfill && (!state.backfill || !state.backfill.completed)) return "backfilling";
|
|
16
28
|
return "live";
|
package/src/status/route.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cause, Effect } from "effect";
|
|
2
|
-
import { HttpRouter, HttpServerResponse } from "effect/
|
|
2
|
+
import { HttpRouter, HttpServerResponse } from "effect/http";
|
|
3
3
|
|
|
4
4
|
import type { ConnectorDefinition } from "../core/types";
|
|
5
5
|
import type { ResourceStatus, StatusUnavailableResponse } from "./types";
|
|
@@ -24,7 +24,7 @@ export const statusRoute = (
|
|
|
24
24
|
Effect.map(
|
|
25
25
|
(state): ResourceStatus => ({
|
|
26
26
|
name: resource.name,
|
|
27
|
-
state: deriveSyncState(resource, state),
|
|
27
|
+
state: deriveSyncState(connector, resource, state),
|
|
28
28
|
...(state?.backfill
|
|
29
29
|
? {
|
|
30
30
|
backfill: {
|
package/src/telemetry.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Cause, Config, Effect, Layer, Metric, Option, type Duration } from "effect";
|
|
2
|
-
import { Headers } from "effect/
|
|
3
|
-
import * as Observability from "effect/
|
|
1
|
+
import { Cause, Config, Effect, Layer, Metric, Option, Schema, type Duration } from "effect";
|
|
2
|
+
import { Headers } from "effect/http";
|
|
3
|
+
import * as Observability from "effect/observability";
|
|
4
4
|
|
|
5
5
|
import { PlatformRuntimeKey } from "./runtime-config/constants";
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ export const SpanName = {
|
|
|
9
9
|
batchProcess: "connector.batch.process",
|
|
10
10
|
webhookDecode: "connector.webhook.decode",
|
|
11
11
|
webhookHandle: "connector.webhook.handle",
|
|
12
|
-
|
|
12
|
+
ingest: "connector.ingest",
|
|
13
13
|
stateSet: "connector.state.set",
|
|
14
14
|
apiFetch: "connector.api.fetch",
|
|
15
15
|
} as const;
|
|
@@ -20,13 +20,13 @@ export const Attr = {
|
|
|
20
20
|
connectorResourcesCount: "airfoil.connector.resources.count",
|
|
21
21
|
resourceName: "airfoil.resource.name",
|
|
22
22
|
resourceSource: "airfoil.resource.source",
|
|
23
|
-
|
|
23
|
+
batchRows: "airfoil.batch.rows",
|
|
24
24
|
batchOutcome: "airfoil.batch.outcome",
|
|
25
25
|
webhookPath: "airfoil.webhook.path",
|
|
26
26
|
webhookOutcome: "airfoil.webhook.outcome",
|
|
27
27
|
syncState: "airfoil.connector.sync.state",
|
|
28
28
|
apiRetryReason: "airfoil.connector.api.retry.reason",
|
|
29
|
-
|
|
29
|
+
ingestionSuccess: "airfoil.ingestion.success",
|
|
30
30
|
stateKey: "airfoil.state.key",
|
|
31
31
|
apiPath: "airfoil.api.path",
|
|
32
32
|
errorPhase: "airfoil.error.phase",
|
|
@@ -58,9 +58,16 @@ const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
|
58
58
|
const maxErrorMessageLength = 500;
|
|
59
59
|
|
|
60
60
|
const OtlpEnvConfig = Config.all({
|
|
61
|
-
enabled: Config.
|
|
62
|
-
baseUrl: Config.option(Config.
|
|
63
|
-
|
|
61
|
+
enabled: Config.Boolean(PlatformRuntimeKey.otelEnabled).pipe(Config.withDefault(false)),
|
|
62
|
+
baseUrl: Config.option(Config.String(PlatformRuntimeKey.otelExporterOtlpEndpoint)),
|
|
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
|
|
|
@@ -223,8 +217,8 @@ export const layer = (config: OtlpTracingConfig = {}, options: OtlpTracingOption
|
|
|
223
217
|
*
|
|
224
218
|
* @example
|
|
225
219
|
* Telemetry.layerConfig({
|
|
226
|
-
* enabled: Config.
|
|
227
|
-
* endpoint: Config.
|
|
220
|
+
* enabled: Config.Boolean("MY_OTEL_ENABLED"),
|
|
221
|
+
* endpoint: Config.String("MY_COLLECTOR_URL"),
|
|
228
222
|
* })
|
|
229
223
|
*/
|
|
230
224
|
export const layerConfig = (
|
|
@@ -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));
|
package/src/webhook/server.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { Cause, Context, Effect, Queue, Schema } from "effect";
|
|
2
|
-
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/
|
|
2
|
+
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
ResourceBatch,
|
|
6
6
|
ResourceDefinition,
|
|
7
|
-
ResourceMutation,
|
|
8
|
-
WebhookHandler,
|
|
9
7
|
WebhookRoute,
|
|
10
8
|
WebhookRouteContext,
|
|
11
9
|
} from "../core/types";
|
|
12
10
|
|
|
13
11
|
import { ConnectorError } from "../errors";
|
|
12
|
+
import { ingestBatch } from "../ingestor/instrumented";
|
|
14
13
|
import * as Metrics from "../metrics";
|
|
15
|
-
import { publishBatch } from "../publisher/instrumented";
|
|
16
14
|
import { Attr, SpanName, annotateError } from "../telemetry";
|
|
17
15
|
|
|
18
16
|
export type QueuedWebhookBatch = {
|
|
@@ -27,39 +25,23 @@ export class WebhookQueue extends Context.Service<
|
|
|
27
25
|
}
|
|
28
26
|
>()("@useairfoil/connector-kit/WebhookQueue") {}
|
|
29
27
|
|
|
30
|
-
const resourceBatch = <Row extends object>(
|
|
31
|
-
|
|
32
|
-
): ResourceBatch<Row> => ({
|
|
33
|
-
mutations,
|
|
28
|
+
const resourceBatch = <Row extends object>(rows: ReadonlyArray<Row>): ResourceBatch<Row> => ({
|
|
29
|
+
rows,
|
|
34
30
|
});
|
|
35
31
|
|
|
36
|
-
const decodeAndHandleWebhook = <Row extends object, Payload>(
|
|
37
|
-
resourceName: string,
|
|
38
|
-
webhook: WebhookHandler<Row, Payload>,
|
|
39
|
-
payload: unknown,
|
|
40
|
-
) =>
|
|
41
|
-
Schema.decodeUnknownEffect(webhook.schema)(payload).pipe(
|
|
42
|
-
Effect.mapError(
|
|
43
|
-
(cause) =>
|
|
44
|
-
new ConnectorError({
|
|
45
|
-
message: `Invalid webhook payload for resource ${resourceName}`,
|
|
46
|
-
cause,
|
|
47
|
-
}),
|
|
48
|
-
),
|
|
49
|
-
Effect.flatMap((decoded) => webhook.handler({ payload: decoded })),
|
|
50
|
-
);
|
|
51
|
-
|
|
52
32
|
const makeRouteContext = (
|
|
53
33
|
request: HttpServerRequest.HttpServerRequest,
|
|
54
34
|
rawBody: Uint8Array,
|
|
55
35
|
payload: unknown,
|
|
56
36
|
batches: Array<QueuedWebhookBatch>,
|
|
37
|
+
resourceNames: ReadonlyArray<string>,
|
|
57
38
|
): WebhookRouteContext<ReadonlyArray<ResourceDefinition>, unknown> => ({
|
|
58
39
|
request,
|
|
59
40
|
rawBody,
|
|
60
41
|
payload,
|
|
61
42
|
to: (resource, payload) =>
|
|
62
43
|
Effect.gen(function* () {
|
|
44
|
+
if (!resourceNames.includes(resource.name)) return;
|
|
63
45
|
if (!resource.webhook) {
|
|
64
46
|
return yield* Effect.fail(
|
|
65
47
|
new ConnectorError({
|
|
@@ -68,27 +50,28 @@ const makeRouteContext = (
|
|
|
68
50
|
);
|
|
69
51
|
}
|
|
70
52
|
|
|
71
|
-
|
|
53
|
+
// The route already decoded the payload, so don't decode it again.
|
|
54
|
+
const rows = yield* resource.webhook.handler({ payload });
|
|
72
55
|
batches.push({
|
|
73
56
|
resource: resource.name,
|
|
74
|
-
batch: resourceBatch(
|
|
57
|
+
batch: resourceBatch(rows),
|
|
75
58
|
});
|
|
76
59
|
}),
|
|
77
60
|
});
|
|
78
61
|
|
|
79
62
|
const compactBatches = (batches: ReadonlyArray<QueuedWebhookBatch>) => {
|
|
80
|
-
const grouped = new Map<string,
|
|
63
|
+
const grouped = new Map<string, object[]>();
|
|
81
64
|
for (const item of batches) {
|
|
82
65
|
const current = grouped.get(item.resource) ?? [];
|
|
83
|
-
grouped.set(item.resource, [...current, ...item.batch.
|
|
66
|
+
grouped.set(item.resource, [...current, ...item.batch.rows]);
|
|
84
67
|
}
|
|
85
|
-
return Array.from(grouped.entries()).map(([resource,
|
|
68
|
+
return Array.from(grouped.entries()).map(([resource, rows]) => ({
|
|
86
69
|
resource,
|
|
87
|
-
batch: resourceBatch(
|
|
70
|
+
batch: resourceBatch(rows),
|
|
88
71
|
}));
|
|
89
72
|
};
|
|
90
73
|
|
|
91
|
-
const
|
|
74
|
+
const ingestBatches = (
|
|
92
75
|
batches: ReadonlyArray<QueuedWebhookBatch>,
|
|
93
76
|
options: { readonly connectorName: string },
|
|
94
77
|
) =>
|
|
@@ -96,7 +79,7 @@ const publishBatches = (
|
|
|
96
79
|
yield* Effect.forEach(
|
|
97
80
|
batches,
|
|
98
81
|
(batch) =>
|
|
99
|
-
|
|
82
|
+
ingestBatch({
|
|
100
83
|
connector: options.connectorName,
|
|
101
84
|
resource: batch.resource,
|
|
102
85
|
source: "webhook",
|
|
@@ -118,7 +101,13 @@ const parseJson = (rawBody: Uint8Array) =>
|
|
|
118
101
|
catch: (cause) => new ConnectorError({ message: "Invalid webhook JSON", cause }),
|
|
119
102
|
});
|
|
120
103
|
|
|
121
|
-
|
|
104
|
+
type RouterOptions = {
|
|
105
|
+
readonly connectorName: string;
|
|
106
|
+
/** Events for other resources are skipped. */
|
|
107
|
+
readonly resourceNames: ReadonlyArray<string>;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const makeHandler = (route: WebhookRoute, options: RouterOptions) =>
|
|
122
111
|
Effect.gen(function* () {
|
|
123
112
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
124
113
|
const collected: Array<QueuedWebhookBatch> = [];
|
|
@@ -129,6 +118,7 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
129
118
|
onSuccess: (value) => ({ _tag: "Success" as const, value }),
|
|
130
119
|
}),
|
|
131
120
|
);
|
|
121
|
+
|
|
132
122
|
if (rawBodyResult._tag === "Error") {
|
|
133
123
|
yield* Metrics.recordWebhookRequest({
|
|
134
124
|
connector: options.connectorName,
|
|
@@ -144,6 +134,7 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
144
134
|
onSuccess: (value) => ({ _tag: "Success" as const, value }),
|
|
145
135
|
}),
|
|
146
136
|
);
|
|
137
|
+
|
|
147
138
|
if (jsonResult._tag === "Error") {
|
|
148
139
|
yield* Metrics.recordWebhookRequest({
|
|
149
140
|
connector: options.connectorName,
|
|
@@ -159,6 +150,7 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
159
150
|
onSuccess: (value) => ({ _tag: "Success" as const, value }),
|
|
160
151
|
}),
|
|
161
152
|
);
|
|
153
|
+
|
|
162
154
|
if (payloadResult._tag === "Error") {
|
|
163
155
|
yield* Metrics.recordWebhookRequest({
|
|
164
156
|
connector: options.connectorName,
|
|
@@ -170,7 +162,15 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
170
162
|
|
|
171
163
|
const response = yield* Effect.withSpan(
|
|
172
164
|
route
|
|
173
|
-
.handler(
|
|
165
|
+
.handler(
|
|
166
|
+
makeRouteContext(
|
|
167
|
+
request,
|
|
168
|
+
rawBodyResult.value,
|
|
169
|
+
payloadResult.value,
|
|
170
|
+
collected,
|
|
171
|
+
options.resourceNames,
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
174
|
.pipe(Effect.tapError((error) => annotateError("webhook_handle", error))),
|
|
175
175
|
SpanName.webhookHandle,
|
|
176
176
|
{ attributes: { [Attr.webhookPath]: route.path } },
|
|
@@ -188,12 +188,13 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
188
188
|
path: String(route.path),
|
|
189
189
|
outcome: response.status >= 400 ? "rejected" : "ok",
|
|
190
190
|
});
|
|
191
|
+
|
|
191
192
|
return response;
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
yield* Effect.withSpan(
|
|
195
|
-
|
|
196
|
-
Effect.tapError((error) => annotateError("
|
|
196
|
+
ingestBatches(batches, { connectorName: options.connectorName }).pipe(
|
|
197
|
+
Effect.tapError((error) => annotateError("webhook_ingest", error)),
|
|
197
198
|
),
|
|
198
199
|
SpanName.webhookHandle,
|
|
199
200
|
{ attributes: { [Attr.webhookPath]: route.path } },
|
|
@@ -204,6 +205,7 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
204
205
|
path: String(route.path),
|
|
205
206
|
outcome: response.status >= 400 ? "rejected" : "ok",
|
|
206
207
|
});
|
|
208
|
+
|
|
207
209
|
return response;
|
|
208
210
|
}).pipe(
|
|
209
211
|
Effect.catchCause((cause) =>
|
|
@@ -225,10 +227,7 @@ const makeHandler = (route: WebhookRoute, options: { readonly connectorName: str
|
|
|
225
227
|
),
|
|
226
228
|
);
|
|
227
229
|
|
|
228
|
-
export const router = (
|
|
229
|
-
routes: ReadonlyArray<WebhookRoute>,
|
|
230
|
-
options: { readonly connectorName: string },
|
|
231
|
-
) =>
|
|
230
|
+
export const router = (routes: ReadonlyArray<WebhookRoute>, options: RouterOptions) =>
|
|
232
231
|
HttpRouter.addAll(
|
|
233
232
|
routes.map((route) => HttpRouter.route("POST", route.path, makeHandler(route, options))),
|
|
234
233
|
);
|
package/dist/auth/index.d.ts
DELETED
package/dist/auth/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-DvU_CZ7C.js","names":[],"sources":["../src/auth/token-cache.ts","../src/auth/index.ts"],"sourcesContent":["import { Cache, Duration, Effect, Exit, Number, Redacted } from \"effect\";\n\nexport interface ExpiringToken {\n /** The credential kept in memory by the cache. */\n readonly value: Redacted.Redacted<string>;\n /** The lifetime reported by the provider. */\n readonly expiresIn: Duration.Duration;\n}\n\nexport interface TokenCache<E> {\n /** Returns the cached token or performs one shared acquisition. */\n readonly get: Effect.Effect<Redacted.Redacted<string>, E>;\n /** Removes the cached token so the next `get` reacquires it. */\n readonly invalidate: Effect.Effect<void>;\n}\n\n/**\n * Creates a lazy, in-memory token cache around an acquisition effect.\n *\n * `refreshFactor` controls how much of the provider lifetime is cached. It is\n * clamped between `0` and `1`.\n *\n * @example\n * ```ts\n * const cache = yield* makeTokenCache({\n * acquire: exchangeCredentials,\n * refreshFactor: 0.5,\n * })\n *\n * const token = yield* cache.get\n * ```\n */\nexport const makeTokenCache = <E, R>(options: {\n readonly acquire: Effect.Effect<ExpiringToken, E, R>;\n readonly refreshFactor?: number;\n}): Effect.Effect<TokenCache<E>, never, R> =>\n Effect.gen(function* () {\n const refreshFactor = Number.clamp(options.refreshFactor ?? 0.5, {\n minimum: 0,\n maximum: 1,\n });\n const cache = yield* Cache.makeWith((_: void) => options.acquire, {\n capacity: 1,\n timeToLive: (exit) =>\n Exit.isSuccess(exit)\n ? Duration.millis(Duration.toMillis(exit.value.expiresIn) * refreshFactor)\n : Duration.zero,\n });\n\n return {\n get: Cache.get(cache, undefined).pipe(Effect.map((token) => token.value)),\n invalidate: Cache.invalidate(cache, undefined),\n };\n });\n","export * from \"./token-cache\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,kBAAwB,YAInC,OAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAO,MAAM,QAAQ,iBAAiB,IAAK;EAC/D,SAAS;EACT,SAAS;EACV,CAAC;CACF,MAAM,QAAQ,OAAO,MAAM,UAAU,MAAY,QAAQ,SAAS;EAChE,UAAU;EACV,aAAa,SACX,KAAK,UAAU,KAAK,GAChB,SAAS,OAAO,SAAS,SAAS,KAAK,MAAM,UAAU,GAAG,cAAc,GACxE,SAAS;EAChB,CAAC;AAEF,QAAO;EACL,KAAK,MAAM,IAAI,OAAO,OAAU,CAAC,KAAK,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC;EACzE,YAAY,MAAM,WAAW,OAAO,OAAU;EAC/C;EACD"}
|