@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,446 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CRON_PATTERN, IDENTIFIER_PATTERN, RESERVED_ENVELOPE_COLUMNS, TIMEZONE_PATTERN, } from '../constants.js';
|
|
3
|
+
import { columnIdentifierSchema, identifierSchema } from '../columns.js';
|
|
4
|
+
import { feedColumnsSchema, feedDocumentationSchema } from '../documentation.js';
|
|
5
|
+
import { operationPolicySchema } from '../operations.js';
|
|
6
|
+
import { isoDurationSchema, repoRelativePathSchema, symbolicRefSchema } from '../primitives.js';
|
|
7
|
+
/**
|
|
8
|
+
* Ingestion stages declare **feeds into a volume landing zone** — never bronze
|
|
9
|
+
* tables, which are platform-derived. This body encodes the append-only-bronze
|
|
10
|
+
* architecture: volumes-first landing, snapshot_mode batch semantics, symbolic
|
|
11
|
+
* worker/credential bindings, and the decode/write split.
|
|
12
|
+
*
|
|
13
|
+
* ## What the platform derives from a feed (not authored)
|
|
14
|
+
*
|
|
15
|
+
* Ingestion lands in **catalog-per-source** catalogs (estate precedent:
|
|
16
|
+
* `src_banksouth_jack_henry`, `src_mock_data`): bronze lives in
|
|
17
|
+
* `src_<source>`, an ACCOUNT-LEVEL asset shared across deployments with a
|
|
18
|
+
* single writing deployment. The deployment catalog (`TargetFacts.catalog`)
|
|
19
|
+
* owns silver/gold/report/serving ONLY and never appears in an ingestion name.
|
|
20
|
+
*
|
|
21
|
+
* - **Source catalog** — `src_<source>` (canonical derivation:
|
|
22
|
+
* `sourceCatalog`; the source identity is underscore-constrained, so the
|
|
23
|
+
* catalog name is deterministic and UC-legal).
|
|
24
|
+
* - **Volume layout** — `/Volumes/src_<source>/bronze/<feed>/batch_id=<ts>/`
|
|
25
|
+
* (UC volumes are `/Volumes/<catalog>/<schema>/<volume>/…`, so the schema
|
|
26
|
+
* is `bronze` and the volume is the feed): every batch lands as raw files
|
|
27
|
+
* under a batch directory keyed by ingestion timestamp. Canonical
|
|
28
|
+
* derivation: `feedVolumePath` (tests pin the documented string to it).
|
|
29
|
+
* - **Bronze table name** — one append-only bronze table per feed:
|
|
30
|
+
* `src_<source>.bronze.<feed>` — the catalog scopes the source, so the
|
|
31
|
+
* table name needs no `<source>_` prefix (canonical derivation:
|
|
32
|
+
* `feedBronzeTable`).
|
|
33
|
+
* - **Envelope columns** — the materializer stamps `batch_id`, `ingested_at`,
|
|
34
|
+
* `source_system`, and `content_hash` onto every row. These names are
|
|
35
|
+
* RESERVED (`RESERVED_ENVELOPE_COLUMNS` in `../constants.js`) — a feed's
|
|
36
|
+
* `natural_key`/`cursor.column` may not name one (`refineFeed` below);
|
|
37
|
+
* the materializer itself rejects a source column collision at runtime,
|
|
38
|
+
* since a feed's full column set is only known at decode time.
|
|
39
|
+
* - **Presence manifest** — for `full_snapshot` feeds the platform records
|
|
40
|
+
* which natural keys appeared in each batch, so downstream deletion
|
|
41
|
+
* detection never needs source-side tombstones.
|
|
42
|
+
*
|
|
43
|
+
* ## The decode/write split
|
|
44
|
+
*
|
|
45
|
+
* Decoders are developer-provided **pure functions (file contents → rows)**
|
|
46
|
+
* invoked by the platform materializer for non-standard formats. Decoders
|
|
47
|
+
* never hold table-write credentials — the platform materializer performs
|
|
48
|
+
* envelope stamping and the append. **Bronze materialization does no semantic
|
|
49
|
+
* work**: no type coercion, dedup, normalization, or filtering — that is
|
|
50
|
+
* cleaning-transformation territory.
|
|
51
|
+
*/
|
|
52
|
+
// Re-exported for existing consumers; the definitions live in primitives.ts
|
|
53
|
+
// so operations.ts can share them without an import cycle.
|
|
54
|
+
export { isoDurationSchema, repoRelativePathSchema, symbolicRefSchema };
|
|
55
|
+
export const SNAPSHOT_MODES = ['full_snapshot', 'incremental', 'event_stream'];
|
|
56
|
+
export const snapshotModeSchema = z.enum(SNAPSHOT_MODES);
|
|
57
|
+
/**
|
|
58
|
+
* Feed formats. `xlsx` is RESERVED: present in the enum but rejected at
|
|
59
|
+
* validation until the platform ships its default spreadsheet decoder (sheet
|
|
60
|
+
* selection / header detection stay byte-shape decoding — type coercion stays
|
|
61
|
+
* in cleaning). `custom` requires a `decoder`.
|
|
62
|
+
*/
|
|
63
|
+
export const FEED_FORMATS = ['parquet', 'json', 'csv', 'xlsx', 'custom'];
|
|
64
|
+
export const feedFormatSchema = z.enum(FEED_FORMATS);
|
|
65
|
+
/**
|
|
66
|
+
* Incremental cursor declaration. `encoding` is an extensible string (e.g.
|
|
67
|
+
* `yyyyddd` for Jack-Henry-style Julian dates) — the SDK stays
|
|
68
|
+
* provider-neutral and never enumerates vendor encodings.
|
|
69
|
+
*/
|
|
70
|
+
export const cursorSchema = z.strictObject({
|
|
71
|
+
/** Source-side cursor column. Source systems may use uppercase names. */
|
|
72
|
+
column: z
|
|
73
|
+
.string()
|
|
74
|
+
.min(1)
|
|
75
|
+
.regex(/^[A-Za-z][A-Za-z0-9_]*$/, 'must be a column identifier'),
|
|
76
|
+
encoding: z.string().min(1).optional(),
|
|
77
|
+
});
|
|
78
|
+
/** Extraction params are scalar hints passed to the extractor (never credentials — the envelope scan enforces it). */
|
|
79
|
+
const extractionValueSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
80
|
+
export const feedSchema = z.strictObject({
|
|
81
|
+
...feedDocumentationSchema.shape,
|
|
82
|
+
/** Optional descriptions of observed source columns; not a typed contract. */
|
|
83
|
+
columns: feedColumnsSchema.optional(),
|
|
84
|
+
/**
|
|
85
|
+
* Optional documentation/<name>.documentation.yml. The loader merges it
|
|
86
|
+
* into this feed, preserving inline overrides, before deployment hashing.
|
|
87
|
+
*/
|
|
88
|
+
documentation_ref: identifierSchema.optional(),
|
|
89
|
+
/**
|
|
90
|
+
* Batch semantics of the feed:
|
|
91
|
+
* - `full_snapshot` — every batch is the complete current state; the
|
|
92
|
+
* platform writes a presence manifest for deletion detection.
|
|
93
|
+
* - `incremental` — batches carry changed rows selected by `cursor`.
|
|
94
|
+
* - `event_stream` — append-only events; no snapshot semantics.
|
|
95
|
+
*/
|
|
96
|
+
snapshot_mode: snapshotModeSchema,
|
|
97
|
+
/** Row identity within the feed. Required (non-empty) for full_snapshot and incremental. */
|
|
98
|
+
natural_key: z.array(columnIdentifierSchema).optional(),
|
|
99
|
+
/** Required iff `snapshot_mode: incremental`; meaningless otherwise. */
|
|
100
|
+
cursor: cursorSchema.optional(),
|
|
101
|
+
pii: z.enum(['none', 'contains_pii']).default('none'),
|
|
102
|
+
/** Wire format of landed files. Standard formats use compiled default decoders. */
|
|
103
|
+
format: feedFormatSchema.optional(),
|
|
104
|
+
/**
|
|
105
|
+
* Repo-relative path of a pure file→rows decoder. Required iff
|
|
106
|
+
* `format: custom`; forbidden otherwise. Decoders never write tables.
|
|
107
|
+
*/
|
|
108
|
+
decoder: repoRelativePathSchema.optional(),
|
|
109
|
+
/** Scalar extraction hints (query params, source table names, page sizes). */
|
|
110
|
+
extraction: z.record(z.string().min(1), extractionValueSchema).optional(),
|
|
111
|
+
/** Max staleness for this feed; overrides the spec-wide `freshness_slo`. */
|
|
112
|
+
freshness_slo: isoDurationSchema.optional(),
|
|
113
|
+
/**
|
|
114
|
+
* The source may omit this feed for a deployment-specific schema/version.
|
|
115
|
+
* Optional feeds remain part of the contract, but extraction and
|
|
116
|
+
* materialization may report a successful no-data skip.
|
|
117
|
+
*/
|
|
118
|
+
optional: z.boolean().optional(),
|
|
119
|
+
});
|
|
120
|
+
/** Feed documentation changes do not change downstream compatibility. */
|
|
121
|
+
export function feedContract(feed) {
|
|
122
|
+
const { description: _description, columns: _columns, column_descriptions: _columnDescriptions, documentation_ref: _documentationRef, ...contract } = feed;
|
|
123
|
+
return contract;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Ingestion schedule: cron + tz + the shared operation policy vocabulary.
|
|
127
|
+
* Optional by design — the future `upload` runtime is event-driven (file
|
|
128
|
+
* arrival), so a schedule is meaningless there.
|
|
129
|
+
*/
|
|
130
|
+
export const scheduleSchema = z.strictObject({
|
|
131
|
+
cron: z.string().regex(CRON_PATTERN, 'must be a cron expression (5–7 fields)'),
|
|
132
|
+
tz: z.string().regex(TIMEZONE_PATTERN, 'must be an IANA timezone (e.g. America/New_York) or UTC'),
|
|
133
|
+
policy: operationPolicySchema.optional(),
|
|
134
|
+
});
|
|
135
|
+
/**
|
|
136
|
+
* Where the ingestion code runs:
|
|
137
|
+
* - `databricks` — a platform-scheduled job in the deployment workspace,
|
|
138
|
+
* pulling from an external system's API (`source` required).
|
|
139
|
+
* - `edge-worker` — an on-prem/VM worker registered in the platform registry
|
|
140
|
+
* (`worker` required): the SEQUENCE2-style extraction posture.
|
|
141
|
+
* - `upload` — RESERVED, rejected at validation ('not yet supported').
|
|
142
|
+
* Future semantics: no entrypoint/schedule/credential; the platform upload
|
|
143
|
+
* surface writes the feed's batch directories; the materializer fires on
|
|
144
|
+
* file arrival (Databricks file-arrival trigger); the batch ledger records
|
|
145
|
+
* the uploading initiator. Covers the spreadsheet→volume→cleaning→ontology
|
|
146
|
+
* expansion (seeds CSVs, iSeries dumps, BSM mock data).
|
|
147
|
+
*/
|
|
148
|
+
export const INGESTION_RUNTIMES = ['databricks', 'edge-worker', 'upload'];
|
|
149
|
+
export const ingestionRuntimeSchema = z.enum(INGESTION_RUNTIMES);
|
|
150
|
+
/**
|
|
151
|
+
* Source-identity slug — feeds UC name derivation (`bronze.<source>_<feed>`,
|
|
152
|
+
* volume paths), so it shares the feed names' lowercase-underscore grammar.
|
|
153
|
+
* Hyphens are legal in symbolic refs but not in UC name parts.
|
|
154
|
+
*/
|
|
155
|
+
const sourceIdentitySchema = z
|
|
156
|
+
.string()
|
|
157
|
+
.max(63)
|
|
158
|
+
.regex(IDENTIFIER_PATTERN, 'must be a lowercase identifier (a-z, 0-9, _) — it becomes part of derived UC names');
|
|
159
|
+
/** Hard ceiling that keeps a malformed authoring file from stampeding a VM. */
|
|
160
|
+
export const MAX_WORKER_CONCURRENT = 32;
|
|
161
|
+
export const DEFAULT_MATERIALIZER_CONCURRENT = 32;
|
|
162
|
+
export const MAX_MATERIALIZER_CONCURRENT = 64;
|
|
163
|
+
export const MATERIALIZER_STRATEGIES = ['per_feed', 'sharded'];
|
|
164
|
+
export const materializerStrategySchema = z.enum(MATERIALIZER_STRATEGIES);
|
|
165
|
+
export const materializerConcurrencySchema = z.strictObject({
|
|
166
|
+
/**
|
|
167
|
+
* Maximum number of feed materializer notebook tasks that may run at once.
|
|
168
|
+
* The compiler enforces this with dependency lanes (`per_feed`) or shard
|
|
169
|
+
* tasks (`sharded`); this is independent of worker.max_concurrent, which
|
|
170
|
+
* limits edge-worker extraction.
|
|
171
|
+
*/
|
|
172
|
+
max_concurrent: z
|
|
173
|
+
.number()
|
|
174
|
+
.int()
|
|
175
|
+
.min(1)
|
|
176
|
+
.max(MAX_MATERIALIZER_CONCURRENT),
|
|
177
|
+
/**
|
|
178
|
+
* How Databricks schedules bronze writes. Omitted = `per_feed` (one
|
|
179
|
+
* notebook task per feed, CON-131 collector when parallel). `sharded`
|
|
180
|
+
* compiles `min(feed_count, max_concurrent)` Spark sessions that each
|
|
181
|
+
* process a disjoint feed subset sequentially (CON-227).
|
|
182
|
+
*/
|
|
183
|
+
strategy: materializerStrategySchema.optional(),
|
|
184
|
+
});
|
|
185
|
+
/**
|
|
186
|
+
* Symbolic worker binding — machine and Data Sync family identifiers, never
|
|
187
|
+
* connection strings or managed-secret bindings.
|
|
188
|
+
*
|
|
189
|
+
* Which fields feed physical-name derivation: `data_source` is the source
|
|
190
|
+
* identity (bronze table names + volume paths derive from it, hence the
|
|
191
|
+
* identifier grammar); `machine` is a registry machine id (SEQUENCE2-style),
|
|
192
|
+
* `credential` a Data Sync environment family, and `operation` a machine-local
|
|
193
|
+
* Data Sync operation id — none appears in any derived name.
|
|
194
|
+
*/
|
|
195
|
+
export const workerSchema = z.strictObject({
|
|
196
|
+
machine: symbolicRefSchema,
|
|
197
|
+
data_source: sourceIdentitySchema,
|
|
198
|
+
credential: symbolicRefSchema,
|
|
199
|
+
/** Optional machine-local Data Sync operation id used by run-now extraction. */
|
|
200
|
+
operation: z
|
|
201
|
+
.string()
|
|
202
|
+
.regex(/^[a-z][a-z0-9_]*$/, 'operation must be a lowercase snake_case Data Sync operation id')
|
|
203
|
+
.optional(),
|
|
204
|
+
/**
|
|
205
|
+
* How many extraction runs the platform may have in flight against this
|
|
206
|
+
* machine + data source at once. One-stage-per-table means N sibling stages
|
|
207
|
+
* contend for ONE ODBC connection on ONE VM, so capacity is a property of the
|
|
208
|
+
* shared upstream, not of any single stage.
|
|
209
|
+
*
|
|
210
|
+
* Enforcement contract (see `workerCapacityGroup`): every stage sharing a
|
|
211
|
+
* machine + data source also shares one `schedule.policy.exclusion_group`, so
|
|
212
|
+
* a consumer that only understands mutual exclusion serializes them —
|
|
213
|
+
* conservative by default. A consumer that understands budgets (the Data Sync
|
|
214
|
+
* enqueue route) may allow up to `max_concurrent` runs of that operation in the
|
|
215
|
+
* group to run together. Different operations in the group remain serialized.
|
|
216
|
+
* Omitted = 1 = strict serialization.
|
|
217
|
+
*/
|
|
218
|
+
max_concurrent: z.number().int().min(1).max(MAX_WORKER_CONCURRENT).optional(),
|
|
219
|
+
});
|
|
220
|
+
/**
|
|
221
|
+
* Symbolic source binding for databricks-runtime API pulls. `system` is the
|
|
222
|
+
* source identity (feeds derived UC names — identifier grammar).
|
|
223
|
+
*
|
|
224
|
+
* `credential` names a binding family when the upstream API is authenticated.
|
|
225
|
+
* Omit it for genuinely keyless / public sources (e.g. ESPN's public site API)
|
|
226
|
+
* — never invent a sentinel binding. Resolution and compiled `credential_*`
|
|
227
|
+
* parameters only occur when this field is present.
|
|
228
|
+
*/
|
|
229
|
+
export const sourceSystemSchema = z.strictObject({
|
|
230
|
+
system: sourceIdentitySchema,
|
|
231
|
+
/**
|
|
232
|
+
* Optional symbolic credential family. Omitted = unauthenticated/keyless
|
|
233
|
+
* source. Never a sentinel value and never a secret.
|
|
234
|
+
*/
|
|
235
|
+
credential: symbolicRefSchema.optional(),
|
|
236
|
+
});
|
|
237
|
+
/** Feed names are snake_case identifiers (they become volume path segments and bronze table parts). */
|
|
238
|
+
export const feedNameSchema = z
|
|
239
|
+
.string()
|
|
240
|
+
.regex(IDENTIFIER_PATTERN, 'feed names must be lowercase identifiers (a-z, 0-9, _)');
|
|
241
|
+
export const ingestionBodyShape = {
|
|
242
|
+
runtime: ingestionRuntimeSchema,
|
|
243
|
+
/** Required iff `runtime: edge-worker`; forbidden otherwise. */
|
|
244
|
+
worker: workerSchema.optional(),
|
|
245
|
+
/** Required iff `runtime: databricks`; forbidden otherwise. */
|
|
246
|
+
source: sourceSystemSchema.optional(),
|
|
247
|
+
/** Databricks-side feed materializer concurrency, independent of extraction. */
|
|
248
|
+
materializer: materializerConcurrencySchema.optional(),
|
|
249
|
+
feeds: z.record(feedNameSchema, feedSchema),
|
|
250
|
+
schedule: scheduleSchema.optional(),
|
|
251
|
+
/** Spec-wide max staleness; feeds may override per-feed. */
|
|
252
|
+
freshness_slo: isoDurationSchema.optional(),
|
|
253
|
+
/** Raw-file retention window — an explicit replay buffer, not an archive. */
|
|
254
|
+
retention: isoDurationSchema.optional(),
|
|
255
|
+
/** Repo-relative code path. Required for databricks / edge-worker runtimes. */
|
|
256
|
+
entrypoint: repoRelativePathSchema.optional(),
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* The source identity a stage ingests from — `source.system` for databricks
|
|
260
|
+
* runtimes, `worker.data_source` for edge workers (stage id as the final
|
|
261
|
+
* fallback for future runtimes). This identity determines EVERY derived
|
|
262
|
+
* bronze name, so it is part of the contract surface (`contract_hash`) and
|
|
263
|
+
* the single input to the derivation helpers below.
|
|
264
|
+
*/
|
|
265
|
+
export function ingestionSourceIdentity(spec) {
|
|
266
|
+
return spec.source?.system ?? spec.worker?.data_source ?? spec.stage;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Canonical per-source catalog: `src_<source>`. Ingestion never lands in the
|
|
270
|
+
* deployment catalog — per-source catalogs are account-level assets shared
|
|
271
|
+
* across deployments (one writing deployment), per the locked topology.
|
|
272
|
+
*
|
|
273
|
+
* Because the catalog is ACCOUNT-level, the source identity is an
|
|
274
|
+
* account-global namespace: where a source system spans tenants, the author
|
|
275
|
+
* scopes the tenant INTO the identity (`banksouth_candescent`, matching the
|
|
276
|
+
* estate's `src_banksouth_jack_henry`); first-party/global sources need no
|
|
277
|
+
* scope (`src_mock_data`). The registry milestone enforces single-writer per
|
|
278
|
+
* `src_*` FQN account-wide and may introduce registry-owned catalog bindings
|
|
279
|
+
* that supersede this deterministic default.
|
|
280
|
+
*/
|
|
281
|
+
export function sourceCatalog(source) {
|
|
282
|
+
return `src_${source}`;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Canonical volume layout for a feed's batches:
|
|
286
|
+
* `/Volumes/src_<source>/bronze/<feed>` — batches land under
|
|
287
|
+
* `batch_id=<ts>/` inside it. The ONE derivation the resolver, docs, and
|
|
288
|
+
* tests share. Derived from the spec's own source identity — never from
|
|
289
|
+
* `TargetFacts.catalog`.
|
|
290
|
+
*/
|
|
291
|
+
export function feedVolumePath(args) {
|
|
292
|
+
return `/Volumes/${sourceCatalog(args.source)}/bronze/${args.feed}`;
|
|
293
|
+
}
|
|
294
|
+
/** Canonical bronze table FQN for a feed: `src_<source>.bronze.<feed>`. */
|
|
295
|
+
export function feedBronzeTable(args) {
|
|
296
|
+
return `${sourceCatalog(args.source)}.bronze.${args.feed}`;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Canonical capacity/exclusion group for every stage extracting from ONE
|
|
300
|
+
* machine + data source: `<machine>_<data_source>`.
|
|
301
|
+
*
|
|
302
|
+
* This is the anti-stampede identity. With one stage per table, ~150 sibling
|
|
303
|
+
* stages point at a single VM holding a single ODBC connection to the core; a
|
|
304
|
+
* per-stage exclusion group would declare them all mutually independent and let
|
|
305
|
+
* every one of them fire at once. Grouping by the contended upstream instead
|
|
306
|
+
* means the declared policy matches physical reality, and `worker.max_concurrent`
|
|
307
|
+
* is the budget within the group.
|
|
308
|
+
*
|
|
309
|
+
* Machine ids permit hyphens (symbolic-ref grammar) while group names are
|
|
310
|
+
* identifiers, so hyphens are normalized to underscores.
|
|
311
|
+
*/
|
|
312
|
+
export function workerCapacityGroup(worker) {
|
|
313
|
+
return `${worker.machine.replace(/-/g, '_')}_${worker.data_source}`;
|
|
314
|
+
}
|
|
315
|
+
/** Cross-field validation for `type: ingestion` bodies (invoked from the envelope refinement). */
|
|
316
|
+
export function refineIngestionBody(body, ctx) {
|
|
317
|
+
if (body.slos?.freshness !== undefined) {
|
|
318
|
+
// One freshness authority per stage kind: ingestion declares freshness in
|
|
319
|
+
// the body (`freshness_slo`, spec-wide or per-feed) — a second envelope
|
|
320
|
+
// knob would create silent precedence questions.
|
|
321
|
+
ctx.addIssue({
|
|
322
|
+
code: 'custom',
|
|
323
|
+
path: ['slos', 'freshness'],
|
|
324
|
+
message: "ingestion stages declare freshness via the body 'freshness_slo' (spec-wide or per-feed) — remove slos.freshness from the envelope",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (body.runtime === 'upload') {
|
|
328
|
+
ctx.addIssue({
|
|
329
|
+
code: 'custom',
|
|
330
|
+
path: ['runtime'],
|
|
331
|
+
message: "runtime 'upload' is reserved, not yet supported — the platform upload surface (file-arrival materialization) has not shipped",
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (body.runtime === 'edge-worker' && !body.worker) {
|
|
336
|
+
ctx.addIssue({
|
|
337
|
+
code: 'custom',
|
|
338
|
+
path: ['worker'],
|
|
339
|
+
message: "runtime 'edge-worker' requires a worker binding { machine, data_source, credential }",
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (body.runtime !== 'edge-worker' && body.worker) {
|
|
343
|
+
ctx.addIssue({
|
|
344
|
+
code: 'custom',
|
|
345
|
+
path: ['worker'],
|
|
346
|
+
message: `worker bindings are only valid for runtime 'edge-worker' (runtime is '${body.runtime}')`,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (body.runtime === 'databricks' && !body.source) {
|
|
350
|
+
ctx.addIssue({
|
|
351
|
+
code: 'custom',
|
|
352
|
+
path: ['source'],
|
|
353
|
+
message: "runtime 'databricks' requires a source binding { system } (credential optional for keyless/public APIs)",
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (body.runtime !== 'databricks' && body.source) {
|
|
357
|
+
ctx.addIssue({
|
|
358
|
+
code: 'custom',
|
|
359
|
+
path: ['source'],
|
|
360
|
+
message: `source bindings are only valid for runtime 'databricks' (runtime is '${body.runtime}')`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
if (!body.entrypoint) {
|
|
364
|
+
ctx.addIssue({
|
|
365
|
+
code: 'custom',
|
|
366
|
+
path: ['entrypoint'],
|
|
367
|
+
message: `runtime '${body.runtime}' requires a repo-relative entrypoint`,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (Object.keys(body.feeds).length === 0) {
|
|
371
|
+
ctx.addIssue({
|
|
372
|
+
code: 'custom',
|
|
373
|
+
path: ['feeds'],
|
|
374
|
+
message: 'an ingestion stage must declare at least one feed',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
for (const [name, feed] of Object.entries(body.feeds)) {
|
|
378
|
+
refineFeed(name, feed, ctx);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function refineFeed(name, feed, ctx) {
|
|
382
|
+
if (feed.snapshot_mode === 'incremental' && !feed.cursor) {
|
|
383
|
+
ctx.addIssue({
|
|
384
|
+
code: 'custom',
|
|
385
|
+
path: ['feeds', name, 'cursor'],
|
|
386
|
+
message: "snapshot_mode 'incremental' requires a cursor { column, encoding? }",
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (feed.snapshot_mode !== 'incremental' && feed.cursor) {
|
|
390
|
+
ctx.addIssue({
|
|
391
|
+
code: 'custom',
|
|
392
|
+
path: ['feeds', name, 'cursor'],
|
|
393
|
+
message: `a cursor is only valid for snapshot_mode 'incremental' (snapshot_mode is '${feed.snapshot_mode}')`,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
if ((feed.snapshot_mode === 'full_snapshot' || feed.snapshot_mode === 'incremental') &&
|
|
397
|
+
(!feed.natural_key || feed.natural_key.length === 0)) {
|
|
398
|
+
ctx.addIssue({
|
|
399
|
+
code: 'custom',
|
|
400
|
+
path: ['feeds', name, 'natural_key'],
|
|
401
|
+
message: `snapshot_mode '${feed.snapshot_mode}' requires a non-empty natural_key`,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (feed.format === 'xlsx') {
|
|
405
|
+
ctx.addIssue({
|
|
406
|
+
code: 'custom',
|
|
407
|
+
path: ['feeds', name, 'format'],
|
|
408
|
+
message: "format 'xlsx' is reserved, not yet supported — the platform's default spreadsheet decoder has not shipped",
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
if (feed.format === 'custom' && !feed.decoder) {
|
|
412
|
+
ctx.addIssue({
|
|
413
|
+
code: 'custom',
|
|
414
|
+
path: ['feeds', name, 'decoder'],
|
|
415
|
+
message: "format 'custom' requires a repo-relative decoder (pure file → rows function)",
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (feed.format !== 'custom' && feed.decoder) {
|
|
419
|
+
ctx.addIssue({
|
|
420
|
+
code: 'custom',
|
|
421
|
+
path: ['feeds', name, 'decoder'],
|
|
422
|
+
message: 'a decoder is only valid with format custom — standard formats use compiled default decoders',
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
// `natural_key`/`cursor.column` are the only source-column names a feed
|
|
426
|
+
// declares (feeds are otherwise schemaless — bronze is raw). The
|
|
427
|
+
// materializer's envelope stamp would silently overwrite and lose a
|
|
428
|
+
// source column sharing one of these reserved names — matched
|
|
429
|
+
// case-insensitively since `cursor.column` (unlike `natural_key`) permits
|
|
430
|
+
// uppercase source-side names. See `RESERVED_ENVELOPE_COLUMNS`.
|
|
431
|
+
const reservedNaturalKeys = (feed.natural_key ?? []).filter((key) => RESERVED_ENVELOPE_COLUMNS.has(key.toLowerCase()));
|
|
432
|
+
const reservedCursorColumn = feed.cursor && RESERVED_ENVELOPE_COLUMNS.has(feed.cursor.column.toLowerCase())
|
|
433
|
+
? [feed.cursor.column]
|
|
434
|
+
: [];
|
|
435
|
+
const reservedCollisions = [...new Set([...reservedNaturalKeys, ...reservedCursorColumn])].sort();
|
|
436
|
+
if (reservedCollisions.length > 0) {
|
|
437
|
+
ctx.addIssue({
|
|
438
|
+
code: 'custom',
|
|
439
|
+
path: ['feeds', name],
|
|
440
|
+
message: `column name(s) ${reservedCollisions.join(', ')} collide with platform-reserved bronze ` +
|
|
441
|
+
`envelope names (${[...RESERVED_ENVELOPE_COLUMNS].sort().join(', ')}) — the materializer ` +
|
|
442
|
+
'stamps these onto every bronze row and would silently overwrite and lose this source data',
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
//# sourceMappingURL=ingestion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingestion.js","sourceRoot":"","sources":["../../src/bodies/ingestion.ts"],"names":[],"mappings":"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"}
|