@sequenceholdings/pipeline-spec 0.1.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/README.md +114 -0
- package/dist/authoring/per-table-ingestion.d.ts +74 -0
- package/dist/authoring/per-table-ingestion.d.ts.map +1 -0
- package/dist/authoring/per-table-ingestion.js +122 -0
- package/dist/authoring/per-table-ingestion.js.map +1 -0
- package/dist/authoring/yaml.d.ts +6 -0
- package/dist/authoring/yaml.d.ts.map +1 -0
- package/dist/authoring/yaml.js +15 -0
- package/dist/authoring/yaml.js.map +1 -0
- package/dist/binding-gaps.d.ts +21 -0
- package/dist/binding-gaps.d.ts.map +1 -0
- package/dist/binding-gaps.js +74 -0
- package/dist/binding-gaps.js.map +1 -0
- package/dist/bodies/ingestion.d.ts +359 -0
- package/dist/bodies/ingestion.d.ts.map +1 -0
- package/dist/bodies/ingestion.js +446 -0
- package/dist/bodies/ingestion.js.map +1 -0
- package/dist/bodies/serving.d.ts +316 -0
- package/dist/bodies/serving.d.ts.map +1 -0
- package/dist/bodies/serving.js +290 -0
- package/dist/bodies/serving.js.map +1 -0
- package/dist/bodies/transformation.d.ts +397 -0
- package/dist/bodies/transformation.d.ts.map +1 -0
- package/dist/bodies/transformation.js +278 -0
- package/dist/bodies/transformation.js.map +1 -0
- package/dist/catalog-binding.d.ts +15 -0
- package/dist/catalog-binding.d.ts.map +1 -0
- package/dist/catalog-binding.js +15 -0
- package/dist/catalog-binding.js.map +1 -0
- package/dist/columns.d.ts +177 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +89 -0
- package/dist/columns.js.map +1 -0
- package/dist/compatibility.d.ts +25 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +108 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/constants.d.ts +95 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +105 -0
- package/dist/constants.js.map +1 -0
- package/dist/diff.d.ts +36 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +300 -0
- package/dist/diff.js.map +1 -0
- package/dist/documentation.d.ts +22 -0
- package/dist/documentation.d.ts.map +1 -0
- package/dist/documentation.js +43 -0
- package/dist/documentation.js.map +1 -0
- package/dist/envelope.d.ts +1241 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +163 -0
- package/dist/envelope.js.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +47 -0
- package/dist/errors.js.map +1 -0
- package/dist/graph-references.d.ts +34 -0
- package/dist/graph-references.d.ts.map +1 -0
- package/dist/graph-references.js +81 -0
- package/dist/graph-references.js.map +1 -0
- package/dist/graph.d.ts +112 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +546 -0
- package/dist/graph.js.map +1 -0
- package/dist/hash.d.ts +28 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +91 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/issues.d.ts +14 -0
- package/dist/issues.d.ts.map +1 -0
- package/dist/issues.js +27 -0
- package/dist/issues.js.map +1 -0
- package/dist/loader.d.ts +50 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +241 -0
- package/dist/loader.js.map +1 -0
- package/dist/migrations/contracts.d.ts +38 -0
- package/dist/migrations/contracts.d.ts.map +1 -0
- package/dist/migrations/contracts.js +175 -0
- package/dist/migrations/contracts.js.map +1 -0
- package/dist/migrations/manifest.d.ts +137 -0
- package/dist/migrations/manifest.d.ts.map +1 -0
- package/dist/migrations/manifest.js +137 -0
- package/dist/migrations/manifest.js.map +1 -0
- package/dist/operations.d.ts +214 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +275 -0
- package/dist/operations.js.map +1 -0
- package/dist/orm-satisfies.d.ts +132 -0
- package/dist/orm-satisfies.d.ts.map +1 -0
- package/dist/orm-satisfies.js +119 -0
- package/dist/orm-satisfies.js.map +1 -0
- package/dist/primitives.d.ts +11 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +20 -0
- package/dist/primitives.js.map +1 -0
- package/dist/resolve.d.ts +124 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +194 -0
- package/dist/resolve.js.map +1 -0
- package/dist/secret-scan.d.ts +34 -0
- package/dist/secret-scan.d.ts.map +1 -0
- package/dist/secret-scan.js +98 -0
- package/dist/secret-scan.js.map +1 -0
- package/dist/serving-compatibility.d.ts +14 -0
- package/dist/serving-compatibility.d.ts.map +1 -0
- package/dist/serving-compatibility.js +19 -0
- package/dist/serving-compatibility.js.map +1 -0
- package/dist/snapshot-transport.d.ts +83 -0
- package/dist/snapshot-transport.d.ts.map +1 -0
- package/dist/snapshot-transport.js +42 -0
- package/dist/snapshot-transport.js.map +1 -0
- package/dist/suggestions.d.ts +3 -0
- package/dist/suggestions.d.ts.map +1 -0
- package/dist/suggestions.js +26 -0
- package/dist/suggestions.js.map +1 -0
- package/dist/types.d.ts +72 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +119 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type CatalogBinding } from '../catalog-binding.js';
|
|
3
|
+
import { type ColumnsOrRef } from '../columns.js';
|
|
4
|
+
/**
|
|
5
|
+
* Transformation stages are where typing begins: inputs by logical asset
|
|
6
|
+
* reference (including the `lakebase_cdf` kind for reverse-sync), engine
|
|
7
|
+
* declaration, and strictly typed outputs whose contracts later compile to
|
|
8
|
+
* DLT expectations and Delta constraints.
|
|
9
|
+
*
|
|
10
|
+
* ## Quarantine semantics
|
|
11
|
+
*
|
|
12
|
+
* Rows failing a `quarantine`-posture check land in a platform-derived
|
|
13
|
+
* `<output>__quarantine` sibling table (see `QUARANTINE_SUFFIX`). The name
|
|
14
|
+
* derivation is defined HERE (M1) so later compilation matches — e.g.
|
|
15
|
+
* `silver.players` quarantines into `silver.players__quarantine`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const TRANSFORMATION_ENGINES: readonly ["dlt", "spark-job", "polars"];
|
|
18
|
+
export declare const transformationEngineSchema: z.ZodEnum<{
|
|
19
|
+
dlt: "dlt";
|
|
20
|
+
"spark-job": "spark-job";
|
|
21
|
+
polars: "polars";
|
|
22
|
+
}>;
|
|
23
|
+
export type TransformationEngine = z.infer<typeof transformationEngineSchema>;
|
|
24
|
+
export declare const outputAssetNameSchema: z.ZodString;
|
|
25
|
+
/**
|
|
26
|
+
* Reference to another stage's asset: an ingestion feed (`feed`) or a
|
|
27
|
+
* transformation output (`output`) — exactly one. `columns` narrows the input
|
|
28
|
+
* to the subset actually consumed (checked against the producer's declared
|
|
29
|
+
* columns by graph validation). `declared_loose` opts out of the column-subset
|
|
30
|
+
* check for schema-fluid sources.
|
|
31
|
+
*/
|
|
32
|
+
declare const stageInputSchema: z.ZodObject<{
|
|
33
|
+
stage: z.ZodString;
|
|
34
|
+
feed: z.ZodOptional<z.ZodString>;
|
|
35
|
+
output: z.ZodOptional<z.ZodString>;
|
|
36
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
37
|
+
freshness_requirement: z.ZodOptional<z.ZodString>;
|
|
38
|
+
declared_loose: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export type StageInput = z.infer<typeof stageInputSchema>;
|
|
41
|
+
/**
|
|
42
|
+
* Platform-defined CDC envelope columns present on every Lakebase CDF history
|
|
43
|
+
* table, IN ADDITION to the declared business columns. Authors never
|
|
44
|
+
* re-declare these; the platform guarantees them.
|
|
45
|
+
*
|
|
46
|
+
* `_pg_change_type` values: `insert`, `update_preimage`, `update_postimage`,
|
|
47
|
+
* `delete`; the commit columns order and deduplicate events.
|
|
48
|
+
*/
|
|
49
|
+
export declare const LAKEBASE_CDF_ENVELOPE_COLUMNS: {
|
|
50
|
+
readonly _pg_change_type: "string";
|
|
51
|
+
readonly _pg_commit_lsn: "string";
|
|
52
|
+
readonly _pg_commit_ts: "timestamp";
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A CDF-enabled Lakebase table input — the reverse-sync path (Postgres manual
|
|
56
|
+
* overlay → Delta history → transformation).
|
|
57
|
+
*
|
|
58
|
+
* The contract is an **envelope, not a row schema**: the input declares the
|
|
59
|
+
* business columns it consumes PLUS the platform-defined CDC envelope
|
|
60
|
+
* (`LAKEBASE_CDF_ENVELOPE_COLUMNS`), and a `fold` semantic:
|
|
61
|
+
*
|
|
62
|
+
* - `latest_state` — the platform-documented fold to current rows by key
|
|
63
|
+
* (last `update_postimage`/`insert` per key, `delete` removes).
|
|
64
|
+
* - `events` — the raw CDC event stream.
|
|
65
|
+
*
|
|
66
|
+
* Business-column types come from the declared schema — never inferred.
|
|
67
|
+
* `manual.*` sources are Atlas-owned tables with no ORM YAML, so columns are
|
|
68
|
+
* ALWAYS declared explicitly. (ORM-YAML-derived typing is deferred until an
|
|
69
|
+
* ORM→Delta type mapping exists — not assumed.)
|
|
70
|
+
*/
|
|
71
|
+
declare const lakebaseCdfInputSchema: z.ZodObject<{
|
|
72
|
+
kind: z.ZodLiteral<"lakebase_cdf">;
|
|
73
|
+
table: z.ZodString;
|
|
74
|
+
columns: z.ZodUnion<readonly [z.ZodObject<{
|
|
75
|
+
schema_ref: z.ZodString;
|
|
76
|
+
}, z.core.$strict>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
77
|
+
type: z.ZodUnion<readonly [z.ZodEnum<{
|
|
78
|
+
string: "string";
|
|
79
|
+
boolean: "boolean";
|
|
80
|
+
uuid: "uuid";
|
|
81
|
+
int: "int";
|
|
82
|
+
long: "long";
|
|
83
|
+
numeric: "numeric";
|
|
84
|
+
double: "double";
|
|
85
|
+
timestamp: "timestamp";
|
|
86
|
+
date: "date";
|
|
87
|
+
json: "json";
|
|
88
|
+
}>, z.ZodEnum<{
|
|
89
|
+
float: "float";
|
|
90
|
+
timestamp_ntz: "timestamp_ntz";
|
|
91
|
+
array: "array";
|
|
92
|
+
map: "map";
|
|
93
|
+
struct: "struct";
|
|
94
|
+
binary: "binary";
|
|
95
|
+
}>, z.ZodString]>;
|
|
96
|
+
nullable: z.ZodDefault<z.ZodBoolean>;
|
|
97
|
+
key: z.ZodDefault<z.ZodBoolean>;
|
|
98
|
+
domain: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
99
|
+
description: z.ZodOptional<z.ZodString>;
|
|
100
|
+
on_violation: z.ZodDefault<z.ZodEnum<{
|
|
101
|
+
fail: "fail";
|
|
102
|
+
quarantine: "quarantine";
|
|
103
|
+
warn: "warn";
|
|
104
|
+
}>>;
|
|
105
|
+
}, z.core.$strict>>]>;
|
|
106
|
+
fold: z.ZodEnum<{
|
|
107
|
+
latest_state: "latest_state";
|
|
108
|
+
events: "events";
|
|
109
|
+
}>;
|
|
110
|
+
}, z.core.$strict>;
|
|
111
|
+
export type LakebaseCdfInput = z.infer<typeof lakebaseCdfInputSchema>;
|
|
112
|
+
/**
|
|
113
|
+
* Discriminated input union: a stage-asset reference or a Lakebase CDF table.
|
|
114
|
+
* (`kind` discriminates; stage references have no `kind`.)
|
|
115
|
+
*/
|
|
116
|
+
export declare const transformationInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
117
|
+
kind: z.ZodLiteral<"lakebase_cdf">;
|
|
118
|
+
table: z.ZodString;
|
|
119
|
+
columns: z.ZodUnion<readonly [z.ZodObject<{
|
|
120
|
+
schema_ref: z.ZodString;
|
|
121
|
+
}, z.core.$strict>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
122
|
+
type: z.ZodUnion<readonly [z.ZodEnum<{
|
|
123
|
+
string: "string";
|
|
124
|
+
boolean: "boolean";
|
|
125
|
+
uuid: "uuid";
|
|
126
|
+
int: "int";
|
|
127
|
+
long: "long";
|
|
128
|
+
numeric: "numeric";
|
|
129
|
+
double: "double";
|
|
130
|
+
timestamp: "timestamp";
|
|
131
|
+
date: "date";
|
|
132
|
+
json: "json";
|
|
133
|
+
}>, z.ZodEnum<{
|
|
134
|
+
float: "float";
|
|
135
|
+
timestamp_ntz: "timestamp_ntz";
|
|
136
|
+
array: "array";
|
|
137
|
+
map: "map";
|
|
138
|
+
struct: "struct";
|
|
139
|
+
binary: "binary";
|
|
140
|
+
}>, z.ZodString]>;
|
|
141
|
+
nullable: z.ZodDefault<z.ZodBoolean>;
|
|
142
|
+
key: z.ZodDefault<z.ZodBoolean>;
|
|
143
|
+
domain: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
144
|
+
description: z.ZodOptional<z.ZodString>;
|
|
145
|
+
on_violation: z.ZodDefault<z.ZodEnum<{
|
|
146
|
+
fail: "fail";
|
|
147
|
+
quarantine: "quarantine";
|
|
148
|
+
warn: "warn";
|
|
149
|
+
}>>;
|
|
150
|
+
}, z.core.$strict>>]>;
|
|
151
|
+
fold: z.ZodEnum<{
|
|
152
|
+
latest_state: "latest_state";
|
|
153
|
+
events: "events";
|
|
154
|
+
}>;
|
|
155
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
156
|
+
stage: z.ZodString;
|
|
157
|
+
feed: z.ZodOptional<z.ZodString>;
|
|
158
|
+
output: z.ZodOptional<z.ZodString>;
|
|
159
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
160
|
+
freshness_requirement: z.ZodOptional<z.ZodString>;
|
|
161
|
+
declared_loose: z.ZodOptional<z.ZodBoolean>;
|
|
162
|
+
}, z.core.$strict>]>;
|
|
163
|
+
export type TransformationInput = z.infer<typeof transformationInputSchema>;
|
|
164
|
+
export declare function isLakebaseCdfInput(input: TransformationInput): input is LakebaseCdfInput;
|
|
165
|
+
/**
|
|
166
|
+
* How the output relation is written — consumed by writer-pattern validation
|
|
167
|
+
* when compilation lands (M6):
|
|
168
|
+
* - `overwrite` — full recompute each run (materialized-view-style).
|
|
169
|
+
* - `append` — append-only (event/log outputs).
|
|
170
|
+
* - `auto_cdc` — merge by key with CDC semantics.
|
|
171
|
+
*/
|
|
172
|
+
export declare const WRITE_PATTERNS: readonly ["overwrite", "append", "auto_cdc"];
|
|
173
|
+
export declare const writePatternSchema: z.ZodEnum<{
|
|
174
|
+
overwrite: "overwrite";
|
|
175
|
+
append: "append";
|
|
176
|
+
auto_cdc: "auto_cdc";
|
|
177
|
+
}>;
|
|
178
|
+
export type WritePattern = z.infer<typeof writePatternSchema>;
|
|
179
|
+
/** A writer owns one disjoint value of a non-null string discriminator. */
|
|
180
|
+
export declare const writeScopeSchema: z.ZodObject<{
|
|
181
|
+
column: z.ZodString;
|
|
182
|
+
value: z.ZodString;
|
|
183
|
+
}, z.core.$strict>;
|
|
184
|
+
export declare const transformationOutputSchema: z.ZodObject<{
|
|
185
|
+
description: z.ZodOptional<z.ZodString>;
|
|
186
|
+
columns: z.ZodUnion<readonly [z.ZodObject<{
|
|
187
|
+
schema_ref: z.ZodString;
|
|
188
|
+
}, z.core.$strict>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
189
|
+
type: z.ZodUnion<readonly [z.ZodEnum<{
|
|
190
|
+
string: "string";
|
|
191
|
+
boolean: "boolean";
|
|
192
|
+
uuid: "uuid";
|
|
193
|
+
int: "int";
|
|
194
|
+
long: "long";
|
|
195
|
+
numeric: "numeric";
|
|
196
|
+
double: "double";
|
|
197
|
+
timestamp: "timestamp";
|
|
198
|
+
date: "date";
|
|
199
|
+
json: "json";
|
|
200
|
+
}>, z.ZodEnum<{
|
|
201
|
+
float: "float";
|
|
202
|
+
timestamp_ntz: "timestamp_ntz";
|
|
203
|
+
array: "array";
|
|
204
|
+
map: "map";
|
|
205
|
+
struct: "struct";
|
|
206
|
+
binary: "binary";
|
|
207
|
+
}>, z.ZodString]>;
|
|
208
|
+
nullable: z.ZodDefault<z.ZodBoolean>;
|
|
209
|
+
key: z.ZodDefault<z.ZodBoolean>;
|
|
210
|
+
domain: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
211
|
+
description: z.ZodOptional<z.ZodString>;
|
|
212
|
+
on_violation: z.ZodDefault<z.ZodEnum<{
|
|
213
|
+
fail: "fail";
|
|
214
|
+
quarantine: "quarantine";
|
|
215
|
+
warn: "warn";
|
|
216
|
+
}>>;
|
|
217
|
+
}, z.core.$strict>>]>;
|
|
218
|
+
on_type_violation: z.ZodDefault<z.ZodEnum<{
|
|
219
|
+
fail: "fail";
|
|
220
|
+
quarantine: "quarantine";
|
|
221
|
+
warn: "warn";
|
|
222
|
+
}>>;
|
|
223
|
+
compatibility: z.ZodDefault<z.ZodEnum<{
|
|
224
|
+
"additive-only": "additive-only";
|
|
225
|
+
versioned: "versioned";
|
|
226
|
+
}>>;
|
|
227
|
+
write_pattern: z.ZodEnum<{
|
|
228
|
+
overwrite: "overwrite";
|
|
229
|
+
append: "append";
|
|
230
|
+
auto_cdc: "auto_cdc";
|
|
231
|
+
}>;
|
|
232
|
+
write_scope: z.ZodOptional<z.ZodObject<{
|
|
233
|
+
column: z.ZodString;
|
|
234
|
+
value: z.ZodString;
|
|
235
|
+
}, z.core.$strict>>;
|
|
236
|
+
}, z.core.$strict>;
|
|
237
|
+
export type TransformationOutput = z.infer<typeof transformationOutputSchema>;
|
|
238
|
+
/** Manual-only, or cron / after (both allowed for cron floor + dependency chaining). */
|
|
239
|
+
export declare const transformationTriggerSchema: z.ZodObject<{
|
|
240
|
+
manual: z.ZodOptional<z.ZodLiteral<true>>;
|
|
241
|
+
cron: z.ZodOptional<z.ZodString>;
|
|
242
|
+
after: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
243
|
+
refresh_serving: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
244
|
+
}, z.core.$strict>;
|
|
245
|
+
export type TransformationTrigger = z.infer<typeof transformationTriggerSchema>;
|
|
246
|
+
export declare const transformationBodyShape: {
|
|
247
|
+
engine: z.ZodEnum<{
|
|
248
|
+
dlt: "dlt";
|
|
249
|
+
"spark-job": "spark-job";
|
|
250
|
+
polars: "polars";
|
|
251
|
+
}>;
|
|
252
|
+
/**
|
|
253
|
+
* The stage's lineage edges — what it consumes. An empty array is legal
|
|
254
|
+
* only as a scaffold state (`seq-studio pipeline init`); graph validation
|
|
255
|
+
* emits a `no_inputs_declared` warning until real inputs are wired.
|
|
256
|
+
*/
|
|
257
|
+
inputs: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
258
|
+
kind: z.ZodLiteral<"lakebase_cdf">;
|
|
259
|
+
table: z.ZodString;
|
|
260
|
+
columns: z.ZodUnion<readonly [z.ZodObject<{
|
|
261
|
+
schema_ref: z.ZodString;
|
|
262
|
+
}, z.core.$strict>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
263
|
+
type: z.ZodUnion<readonly [z.ZodEnum<{
|
|
264
|
+
string: "string";
|
|
265
|
+
boolean: "boolean";
|
|
266
|
+
uuid: "uuid";
|
|
267
|
+
int: "int";
|
|
268
|
+
long: "long";
|
|
269
|
+
numeric: "numeric";
|
|
270
|
+
double: "double";
|
|
271
|
+
timestamp: "timestamp";
|
|
272
|
+
date: "date";
|
|
273
|
+
json: "json";
|
|
274
|
+
}>, z.ZodEnum<{
|
|
275
|
+
float: "float";
|
|
276
|
+
timestamp_ntz: "timestamp_ntz";
|
|
277
|
+
array: "array";
|
|
278
|
+
map: "map";
|
|
279
|
+
struct: "struct";
|
|
280
|
+
binary: "binary";
|
|
281
|
+
}>, z.ZodString]>;
|
|
282
|
+
nullable: z.ZodDefault<z.ZodBoolean>;
|
|
283
|
+
key: z.ZodDefault<z.ZodBoolean>;
|
|
284
|
+
domain: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
285
|
+
description: z.ZodOptional<z.ZodString>;
|
|
286
|
+
on_violation: z.ZodDefault<z.ZodEnum<{
|
|
287
|
+
fail: "fail";
|
|
288
|
+
quarantine: "quarantine";
|
|
289
|
+
warn: "warn";
|
|
290
|
+
}>>;
|
|
291
|
+
}, z.core.$strict>>]>;
|
|
292
|
+
fold: z.ZodEnum<{
|
|
293
|
+
latest_state: "latest_state";
|
|
294
|
+
events: "events";
|
|
295
|
+
}>;
|
|
296
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
297
|
+
stage: z.ZodString;
|
|
298
|
+
feed: z.ZodOptional<z.ZodString>;
|
|
299
|
+
output: z.ZodOptional<z.ZodString>;
|
|
300
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
301
|
+
freshness_requirement: z.ZodOptional<z.ZodString>;
|
|
302
|
+
declared_loose: z.ZodOptional<z.ZodBoolean>;
|
|
303
|
+
}, z.core.$strict>]>>;
|
|
304
|
+
trigger: z.ZodObject<{
|
|
305
|
+
manual: z.ZodOptional<z.ZodLiteral<true>>;
|
|
306
|
+
cron: z.ZodOptional<z.ZodString>;
|
|
307
|
+
after: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
308
|
+
refresh_serving: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
309
|
+
}, z.core.$strict>;
|
|
310
|
+
outputs: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
311
|
+
description: z.ZodOptional<z.ZodString>;
|
|
312
|
+
columns: z.ZodUnion<readonly [z.ZodObject<{
|
|
313
|
+
schema_ref: z.ZodString;
|
|
314
|
+
}, z.core.$strict>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
315
|
+
type: z.ZodUnion<readonly [z.ZodEnum<{
|
|
316
|
+
string: "string";
|
|
317
|
+
boolean: "boolean";
|
|
318
|
+
uuid: "uuid";
|
|
319
|
+
int: "int";
|
|
320
|
+
long: "long";
|
|
321
|
+
numeric: "numeric";
|
|
322
|
+
double: "double";
|
|
323
|
+
timestamp: "timestamp";
|
|
324
|
+
date: "date";
|
|
325
|
+
json: "json";
|
|
326
|
+
}>, z.ZodEnum<{
|
|
327
|
+
float: "float";
|
|
328
|
+
timestamp_ntz: "timestamp_ntz";
|
|
329
|
+
array: "array";
|
|
330
|
+
map: "map";
|
|
331
|
+
struct: "struct";
|
|
332
|
+
binary: "binary";
|
|
333
|
+
}>, z.ZodString]>;
|
|
334
|
+
nullable: z.ZodDefault<z.ZodBoolean>;
|
|
335
|
+
key: z.ZodDefault<z.ZodBoolean>;
|
|
336
|
+
domain: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
337
|
+
description: z.ZodOptional<z.ZodString>;
|
|
338
|
+
on_violation: z.ZodDefault<z.ZodEnum<{
|
|
339
|
+
fail: "fail";
|
|
340
|
+
quarantine: "quarantine";
|
|
341
|
+
warn: "warn";
|
|
342
|
+
}>>;
|
|
343
|
+
}, z.core.$strict>>]>;
|
|
344
|
+
on_type_violation: z.ZodDefault<z.ZodEnum<{
|
|
345
|
+
fail: "fail";
|
|
346
|
+
quarantine: "quarantine";
|
|
347
|
+
warn: "warn";
|
|
348
|
+
}>>;
|
|
349
|
+
compatibility: z.ZodDefault<z.ZodEnum<{
|
|
350
|
+
"additive-only": "additive-only";
|
|
351
|
+
versioned: "versioned";
|
|
352
|
+
}>>;
|
|
353
|
+
write_pattern: z.ZodEnum<{
|
|
354
|
+
overwrite: "overwrite";
|
|
355
|
+
append: "append";
|
|
356
|
+
auto_cdc: "auto_cdc";
|
|
357
|
+
}>;
|
|
358
|
+
write_scope: z.ZodOptional<z.ZodObject<{
|
|
359
|
+
column: z.ZodString;
|
|
360
|
+
value: z.ZodString;
|
|
361
|
+
}, z.core.$strict>>;
|
|
362
|
+
}, z.core.$strict>>;
|
|
363
|
+
entrypoint: z.ZodString;
|
|
364
|
+
config: z.ZodOptional<z.ZodObject<{
|
|
365
|
+
catalog: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
366
|
+
default: z.ZodString;
|
|
367
|
+
by_target: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
368
|
+
}, z.core.$strict>]>>;
|
|
369
|
+
history_catalog: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
370
|
+
default: z.ZodString;
|
|
371
|
+
by_target: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
372
|
+
}, z.core.$strict>]>>;
|
|
373
|
+
silver_catalog: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
374
|
+
default: z.ZodString;
|
|
375
|
+
by_target: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
376
|
+
}, z.core.$strict>]>>;
|
|
377
|
+
python_environment_version: z.ZodOptional<z.ZodEnum<{
|
|
378
|
+
3: "3";
|
|
379
|
+
4: "4";
|
|
380
|
+
}>>;
|
|
381
|
+
}, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>>;
|
|
382
|
+
};
|
|
383
|
+
/** Structural slice of a stage spec the transformation refinement needs. */
|
|
384
|
+
export interface TransformationBody {
|
|
385
|
+
readonly engine: TransformationEngine;
|
|
386
|
+
readonly inputs: readonly TransformationInput[];
|
|
387
|
+
readonly trigger: TransformationTrigger;
|
|
388
|
+
readonly outputs: Record<string, {
|
|
389
|
+
columns: ColumnsOrRef;
|
|
390
|
+
write_scope?: z.infer<typeof writeScopeSchema>;
|
|
391
|
+
}>;
|
|
392
|
+
readonly config?: Readonly<Record<string, string | number | boolean | CatalogBinding>>;
|
|
393
|
+
}
|
|
394
|
+
/** Cross-field validation for `type: transformation` bodies (invoked from the envelope refinement). */
|
|
395
|
+
export declare function refineTransformationBody(body: TransformationBody, ctx: z.RefinementCtx): void;
|
|
396
|
+
export {};
|
|
397
|
+
//# sourceMappingURL=transformation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformation.d.ts","sourceRoot":"","sources":["../../src/bodies/transformation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { catalogBindingSchema } from '../catalog-binding.js';
|
|
3
|
+
import { CRON_PATTERN, OUTPUT_ASSET_PATTERN, QUALIFIED_TABLE_PATTERN, REPORT_ASSET_PATTERN, QUARANTINE_SUFFIX, STAGE_ID_PATTERN, } from '../constants.js';
|
|
4
|
+
import { columnIdentifierSchema, columnsOrRefSchema, identifierSchema, isSchemaRef, nullableKeyColumns, } from '../columns.js';
|
|
5
|
+
import { isoDurationSchema, repoRelativePathSchema } from './ingestion.js';
|
|
6
|
+
/**
|
|
7
|
+
* Transformation stages are where typing begins: inputs by logical asset
|
|
8
|
+
* reference (including the `lakebase_cdf` kind for reverse-sync), engine
|
|
9
|
+
* declaration, and strictly typed outputs whose contracts later compile to
|
|
10
|
+
* DLT expectations and Delta constraints.
|
|
11
|
+
*
|
|
12
|
+
* ## Quarantine semantics
|
|
13
|
+
*
|
|
14
|
+
* Rows failing a `quarantine`-posture check land in a platform-derived
|
|
15
|
+
* `<output>__quarantine` sibling table (see `QUARANTINE_SUFFIX`). The name
|
|
16
|
+
* derivation is defined HERE (M1) so later compilation matches — e.g.
|
|
17
|
+
* `silver.players` quarantines into `silver.players__quarantine`.
|
|
18
|
+
*/
|
|
19
|
+
export const TRANSFORMATION_ENGINES = ['dlt', 'spark-job', 'polars'];
|
|
20
|
+
export const transformationEngineSchema = z.enum(TRANSFORMATION_ENGINES);
|
|
21
|
+
const stageRefSchema = z
|
|
22
|
+
.string()
|
|
23
|
+
.regex(STAGE_ID_PATTERN, 'must be a stage id (kebab-case slug)');
|
|
24
|
+
export const outputAssetNameSchema = z
|
|
25
|
+
.string()
|
|
26
|
+
.regex(OUTPUT_ASSET_PATTERN, "output assets must match '^(silver|gold|report)\\.[a-z][a-z0-9_]*$'");
|
|
27
|
+
/**
|
|
28
|
+
* Reference to another stage's asset: an ingestion feed (`feed`) or a
|
|
29
|
+
* transformation output (`output`) — exactly one. `columns` narrows the input
|
|
30
|
+
* to the subset actually consumed (checked against the producer's declared
|
|
31
|
+
* columns by graph validation). `declared_loose` opts out of the column-subset
|
|
32
|
+
* check for schema-fluid sources.
|
|
33
|
+
*/
|
|
34
|
+
const stageInputSchema = z.strictObject({
|
|
35
|
+
stage: stageRefSchema,
|
|
36
|
+
feed: identifierSchema.optional(),
|
|
37
|
+
output: outputAssetNameSchema.optional(),
|
|
38
|
+
columns: z.array(columnIdentifierSchema).min(1).optional(),
|
|
39
|
+
/** Max acceptable staleness of the consumed asset. */
|
|
40
|
+
freshness_requirement: isoDurationSchema.optional(),
|
|
41
|
+
/** Skip column-subset validation — the input consumes whatever the producer emits. */
|
|
42
|
+
declared_loose: z.boolean().optional(),
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Platform-defined CDC envelope columns present on every Lakebase CDF history
|
|
46
|
+
* table, IN ADDITION to the declared business columns. Authors never
|
|
47
|
+
* re-declare these; the platform guarantees them.
|
|
48
|
+
*
|
|
49
|
+
* `_pg_change_type` values: `insert`, `update_preimage`, `update_postimage`,
|
|
50
|
+
* `delete`; the commit columns order and deduplicate events.
|
|
51
|
+
*/
|
|
52
|
+
export const LAKEBASE_CDF_ENVELOPE_COLUMNS = {
|
|
53
|
+
_pg_change_type: 'string',
|
|
54
|
+
_pg_commit_lsn: 'string',
|
|
55
|
+
_pg_commit_ts: 'timestamp',
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* A CDF-enabled Lakebase table input — the reverse-sync path (Postgres manual
|
|
59
|
+
* overlay → Delta history → transformation).
|
|
60
|
+
*
|
|
61
|
+
* The contract is an **envelope, not a row schema**: the input declares the
|
|
62
|
+
* business columns it consumes PLUS the platform-defined CDC envelope
|
|
63
|
+
* (`LAKEBASE_CDF_ENVELOPE_COLUMNS`), and a `fold` semantic:
|
|
64
|
+
*
|
|
65
|
+
* - `latest_state` — the platform-documented fold to current rows by key
|
|
66
|
+
* (last `update_postimage`/`insert` per key, `delete` removes).
|
|
67
|
+
* - `events` — the raw CDC event stream.
|
|
68
|
+
*
|
|
69
|
+
* Business-column types come from the declared schema — never inferred.
|
|
70
|
+
* `manual.*` sources are Atlas-owned tables with no ORM YAML, so columns are
|
|
71
|
+
* ALWAYS declared explicitly. (ORM-YAML-derived typing is deferred until an
|
|
72
|
+
* ORM→Delta type mapping exists — not assumed.)
|
|
73
|
+
*/
|
|
74
|
+
const lakebaseCdfInputSchema = z.strictObject({
|
|
75
|
+
kind: z.literal('lakebase_cdf'),
|
|
76
|
+
/** `<schema>.<table>` of the CDF-enabled Lakebase source (e.g. `manual.retail_customers`). */
|
|
77
|
+
table: z
|
|
78
|
+
.string()
|
|
79
|
+
.regex(QUALIFIED_TABLE_PATTERN, "must be '<schema>.<table>' (e.g. manual.retail_customers)"),
|
|
80
|
+
/** Business columns consumed, with types — REQUIRED, never inferred. */
|
|
81
|
+
columns: columnsOrRefSchema,
|
|
82
|
+
fold: z.enum(['latest_state', 'events']),
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Discriminated input union: a stage-asset reference or a Lakebase CDF table.
|
|
86
|
+
* (`kind` discriminates; stage references have no `kind`.)
|
|
87
|
+
*/
|
|
88
|
+
export const transformationInputSchema = z.union([lakebaseCdfInputSchema, stageInputSchema]);
|
|
89
|
+
export function isLakebaseCdfInput(input) {
|
|
90
|
+
return 'kind' in input && input.kind === 'lakebase_cdf';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* How the output relation is written — consumed by writer-pattern validation
|
|
94
|
+
* when compilation lands (M6):
|
|
95
|
+
* - `overwrite` — full recompute each run (materialized-view-style).
|
|
96
|
+
* - `append` — append-only (event/log outputs).
|
|
97
|
+
* - `auto_cdc` — merge by key with CDC semantics.
|
|
98
|
+
*/
|
|
99
|
+
export const WRITE_PATTERNS = ['overwrite', 'append', 'auto_cdc'];
|
|
100
|
+
export const writePatternSchema = z.enum(WRITE_PATTERNS);
|
|
101
|
+
/** A writer owns one disjoint value of a non-null string discriminator. */
|
|
102
|
+
export const writeScopeSchema = z.strictObject({
|
|
103
|
+
column: columnIdentifierSchema,
|
|
104
|
+
value: z.string().min(1),
|
|
105
|
+
});
|
|
106
|
+
export const transformationOutputSchema = z.strictObject({
|
|
107
|
+
description: z.string().optional(),
|
|
108
|
+
/** The output's typed contract — inline columns or a `schema_ref`. */
|
|
109
|
+
columns: columnsOrRefSchema,
|
|
110
|
+
/**
|
|
111
|
+
* Row posture when a value violates its column type: `fail` the batch
|
|
112
|
+
* (default), `quarantine` into `<output>__quarantine`, or `warn`.
|
|
113
|
+
*/
|
|
114
|
+
on_type_violation: z.enum(['fail', 'quarantine', 'warn']).default('fail'),
|
|
115
|
+
/**
|
|
116
|
+
* Contract evolution posture: `additive-only` (default) — breaking changes
|
|
117
|
+
* fail validation; `versioned` — breaking changes allowed with an explicit
|
|
118
|
+
* version bump (enforced by the CI gate via `diffContracts`).
|
|
119
|
+
*/
|
|
120
|
+
compatibility: z.enum(['additive-only', 'versioned']).default('additive-only'),
|
|
121
|
+
write_pattern: writePatternSchema,
|
|
122
|
+
/** Every write, including snapshot deletions, must stay inside this scope. */
|
|
123
|
+
write_scope: writeScopeSchema.optional(),
|
|
124
|
+
});
|
|
125
|
+
/** Manual-only, or cron / after (both allowed for cron floor + dependency chaining). */
|
|
126
|
+
export const transformationTriggerSchema = z.strictObject({
|
|
127
|
+
/** Explicit opt-in to run-now only; useful for isolated parity and backfill jobs. */
|
|
128
|
+
manual: z.literal(true).optional(),
|
|
129
|
+
cron: z.string().regex(CRON_PATTERN, 'must be a cron expression (5–7 fields)').optional(),
|
|
130
|
+
after: z
|
|
131
|
+
.array(stageRefSchema)
|
|
132
|
+
.min(1)
|
|
133
|
+
.refine((after) => new Set(after).size === after.length, 'after must not declare the same upstream stage twice')
|
|
134
|
+
.optional(),
|
|
135
|
+
/** Refresh already provisioned serving stages that read this writer's outputs. */
|
|
136
|
+
refresh_serving: z.array(stageRefSchema).min(1)
|
|
137
|
+
.refine((stages) => new Set(stages).size === stages.length, 'refresh_serving must not repeat a stage')
|
|
138
|
+
.optional(),
|
|
139
|
+
});
|
|
140
|
+
/** Opaque per-engine scalar config plus platform-owned catalog bindings. */
|
|
141
|
+
const engineConfigValueSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
142
|
+
const engineConfigSchema = z
|
|
143
|
+
.object({
|
|
144
|
+
catalog: catalogBindingSchema.optional(),
|
|
145
|
+
history_catalog: catalogBindingSchema.optional(),
|
|
146
|
+
silver_catalog: catalogBindingSchema.optional(),
|
|
147
|
+
/** Explicit serverless Python environment; omitted preserves version 3. */
|
|
148
|
+
python_environment_version: z.enum(['3', '4']).optional(),
|
|
149
|
+
})
|
|
150
|
+
.catchall(engineConfigValueSchema);
|
|
151
|
+
export const transformationBodyShape = {
|
|
152
|
+
engine: transformationEngineSchema,
|
|
153
|
+
/**
|
|
154
|
+
* The stage's lineage edges — what it consumes. An empty array is legal
|
|
155
|
+
* only as a scaffold state (`seq-studio pipeline init`); graph validation
|
|
156
|
+
* emits a `no_inputs_declared` warning until real inputs are wired.
|
|
157
|
+
*/
|
|
158
|
+
inputs: z.array(transformationInputSchema),
|
|
159
|
+
trigger: transformationTriggerSchema,
|
|
160
|
+
outputs: z.record(outputAssetNameSchema, transformationOutputSchema),
|
|
161
|
+
entrypoint: repoRelativePathSchema,
|
|
162
|
+
config: engineConfigSchema.optional(),
|
|
163
|
+
};
|
|
164
|
+
const CATALOG_CONFIG_KEYS = ['bronze_catalog'];
|
|
165
|
+
function refineCatalogConfigKeys(config, ctx) {
|
|
166
|
+
if (!config)
|
|
167
|
+
return;
|
|
168
|
+
for (const key of CATALOG_CONFIG_KEYS) {
|
|
169
|
+
if (!(key in config))
|
|
170
|
+
continue;
|
|
171
|
+
const parsed = identifierSchema.safeParse(config[key]);
|
|
172
|
+
if (parsed.success)
|
|
173
|
+
continue;
|
|
174
|
+
ctx.addIssue({
|
|
175
|
+
code: 'custom',
|
|
176
|
+
path: ['config', key],
|
|
177
|
+
message: parsed.error.issues[0]?.message ?? 'must be a lowercase identifier (a-z, 0-9, _)',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** Cross-field validation for `type: transformation` bodies (invoked from the envelope refinement). */
|
|
182
|
+
export function refineTransformationBody(body, ctx) {
|
|
183
|
+
refineCatalogConfigKeys(body.config, ctx);
|
|
184
|
+
if (body.config?.python_environment_version !== undefined && body.engine !== 'polars') {
|
|
185
|
+
ctx.addIssue({ code: 'custom', path: ['config', 'python_environment_version'], message: 'python_environment_version requires a polars producer' });
|
|
186
|
+
}
|
|
187
|
+
if (body.trigger.refresh_serving && body.engine !== 'spark-job' && body.engine !== 'polars') {
|
|
188
|
+
ctx.addIssue({ code: 'custom', path: ['trigger', 'refresh_serving'], message: 'refresh_serving requires a spark-job or polars producer' });
|
|
189
|
+
}
|
|
190
|
+
if (body.trigger.manual && (body.trigger.cron || body.trigger.after || body.trigger.refresh_serving)) {
|
|
191
|
+
ctx.addIssue({ code: 'custom', path: ['trigger'], message: 'manual cannot be combined with cron, after, or refresh_serving' });
|
|
192
|
+
}
|
|
193
|
+
if (!body.trigger.manual && !body.trigger.cron && (!body.trigger.after || body.trigger.after.length === 0)) {
|
|
194
|
+
ctx.addIssue({
|
|
195
|
+
code: 'custom',
|
|
196
|
+
path: ['trigger'],
|
|
197
|
+
message: 'a transformation must declare a trigger: cron, after (upstream stages), both, or manual',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
body.inputs.forEach((input, index) => {
|
|
201
|
+
if (isLakebaseCdfInput(input))
|
|
202
|
+
return;
|
|
203
|
+
const hasFeed = input.feed !== undefined;
|
|
204
|
+
const hasOutput = input.output !== undefined;
|
|
205
|
+
if (hasFeed === hasOutput) {
|
|
206
|
+
ctx.addIssue({
|
|
207
|
+
code: 'custom',
|
|
208
|
+
path: ['inputs', index],
|
|
209
|
+
message: "a stage input must reference exactly one of 'feed' (ingestion feed) or 'output' (transformation output)",
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// Inventory-driven raw passthroughs (FIS/JH report plants) write table
|
|
214
|
+
// names from code. Declaring each report.* output here is what OOM'd
|
|
215
|
+
// pipeline-deploy during bundle deploy.
|
|
216
|
+
if (Object.keys(body.outputs).length === 0 && body.config?.raw_passthrough !== true) {
|
|
217
|
+
ctx.addIssue({
|
|
218
|
+
code: 'custom',
|
|
219
|
+
path: ['outputs'],
|
|
220
|
+
message: 'a transformation must declare at least one output',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
for (const [asset, output] of Object.entries(body.outputs)) {
|
|
224
|
+
if (REPORT_ASSET_PATTERN.test(asset) && body.config?.raw_passthrough !== true) {
|
|
225
|
+
ctx.addIssue({
|
|
226
|
+
code: 'custom',
|
|
227
|
+
path: ['config', 'raw_passthrough'],
|
|
228
|
+
message: `report output '${asset}' requires config.raw_passthrough: true`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (!REPORT_ASSET_PATTERN.test(asset) && body.config?.raw_passthrough === true) {
|
|
232
|
+
ctx.addIssue({
|
|
233
|
+
code: 'custom',
|
|
234
|
+
path: ['outputs', asset],
|
|
235
|
+
message: `raw passthrough stages may only declare report.* outputs (got '${asset}')`,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
refineWriteScope({ engine: body.engine, asset, output, ctx });
|
|
239
|
+
if (asset.endsWith(QUARANTINE_SUFFIX)) {
|
|
240
|
+
ctx.addIssue({
|
|
241
|
+
code: 'custom',
|
|
242
|
+
path: ['outputs', asset],
|
|
243
|
+
message: `'${QUARANTINE_SUFFIX}' names are platform-derived quarantine tables and cannot be declared`,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (isSchemaRef(output.columns))
|
|
247
|
+
continue;
|
|
248
|
+
const badKeys = nullableKeyColumns(output.columns);
|
|
249
|
+
for (const column of badKeys) {
|
|
250
|
+
ctx.addIssue({
|
|
251
|
+
code: 'custom',
|
|
252
|
+
path: ['outputs', asset, 'columns', column, 'nullable'],
|
|
253
|
+
message: `key column '${column}' must be nullable: false`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/** Scope declarations are valid only for explicit atomic job writers. */
|
|
259
|
+
function refineWriteScope({ engine, asset, output, ctx }) {
|
|
260
|
+
if (!output.write_scope)
|
|
261
|
+
return;
|
|
262
|
+
if (engine !== 'spark-job' && engine !== 'polars') {
|
|
263
|
+
ctx.addIssue({ code: 'custom', path: ['outputs', asset, 'write_scope'],
|
|
264
|
+
message: 'scoped writers require spark-job or polars with source-scoped atomic writes' });
|
|
265
|
+
}
|
|
266
|
+
if (isSchemaRef(output.columns))
|
|
267
|
+
return;
|
|
268
|
+
const column = output.columns[output.write_scope.column];
|
|
269
|
+
if (!column || column.type !== 'string' || column.nullable) {
|
|
270
|
+
ctx.addIssue({ code: 'custom', path: ['outputs', asset, 'write_scope'],
|
|
271
|
+
message: 'write_scope.column must name a non-nullable string output column' });
|
|
272
|
+
}
|
|
273
|
+
else if (column.domain && !column.domain.includes(output.write_scope.value)) {
|
|
274
|
+
ctx.addIssue({ code: 'custom', path: ['outputs', asset, 'write_scope', 'value'],
|
|
275
|
+
message: 'write_scope.value must belong to the discriminator column domain' });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=transformation.js.map
|