|
| 1 | +--- |
| 2 | +"@objectstack/spec": major |
| 3 | +--- |
| 4 | + |
| 5 | +**`automation/etl.zod.ts`'s nine type aliases now follow the house `X` / `XParsed` |
| 6 | +convention** (#4963): the bare name is `z.input` — what an author writes — and a new |
| 7 | +`XParsed` is `z.infer` — what a parse returns. |
| 8 | + |
| 9 | +Until 17 all nine were `z.infer` under the bare name with no `*Parsed` counterpart at |
| 10 | +all. On this file that was not a style detail. Five named keys carry `.default()` |
| 11 | +(`ETLDestination.writeMode`, `ETLTransformation.continueOnError`, `ETLPipeline.syncMode` |
| 12 | +/ `.enabled`, `ETLSource.incremental.enabled`), as does every key of |
| 13 | +`ETLPipeline.retry` and of `ETLPipelineRun.stats`, and `schedule` is a |
| 14 | +`CronExpressionInputSchema` transform whose *output* is the `{ dialect, source }` |
| 15 | +envelope. Under `z.infer` all of them were REQUIRED and a bare cron string was rejected — so the single use this file has, `const p: ETLPipeline = { … }` |
| 16 | +written by hand, did not compile. That is the whole authoring door: `etl.zod.ts` has no |
| 17 | +parse site in objectstack / objectui / cloud, so the exported schema and the exported |
| 18 | +type are the only surface an author touches. |
| 19 | + |
| 20 | +The evidence was checked in. `packages/spec/docs/SYNC_ARCHITECTURE.md` carried three |
| 21 | +`ETLPipeline` examples, none of which compiled, and both `ETL` factories were spelling |
| 22 | +out defaults and pre-wrapping their cron purely to satisfy their own return type. |
| 23 | + |
| 24 | +### Migration |
| 25 | + |
| 26 | +**Zero importers across objectstack, objectui and cloud** (re-measured against each |
| 27 | +repo's `origin/main` for this change), so the migration surface is empty. If you have a |
| 28 | +local consumer: |
| 29 | + |
| 30 | +| You wrote | Keep it if | Change it to | |
| 31 | +|:---|:---|:---| |
| 32 | +| `const p: ETLPipeline = { … }` | you are AUTHORING a pipeline literal | nothing — this is the case that now compiles | |
| 33 | +| `const p: ETLPipeline = ETLPipelineSchema.parse(raw)` | — | `const p: ETLPipelineParsed = …` | |
| 34 | +| `function run(p: ETLPipeline)` reading `p.syncMode` as always-present | — | `ETLPipelineParsed` | |
| 35 | + |
| 36 | +The same rename applies to each of the nine: `ETLEndpointType`, `ETLSource`, |
| 37 | +`ETLDestination`, `ETLTransformationType`, `ETLTransformation`, `ETLSyncMode`, |
| 38 | +`ETLPipeline`, `ETLRunStatus`, `ETLPipelineRun` — append `Parsed` wherever the annotated |
| 39 | +value came out of a `.parse()`. The four enum aliases are unaffected in practice |
| 40 | +(`z.input` and `z.infer` coincide for an enum); their pair exists so a reader never has |
| 41 | +to know which of the nine has defaults before choosing an annotation. |
| 42 | + |
| 43 | +Nothing at runtime moves: no schema, default, bound or key changed, and both `ETL` |
| 44 | +factories still produce documents that parse to the same result. `ETL.databaseSync` / |
| 45 | +`ETL.apiToDatabase` no longer restate `enabled: true` and no longer pre-wrap a bare cron |
| 46 | +string into `{ dialect: 'cron', source }` — the schema does that at parse, which is where |
| 47 | +it always belonged. Each helper still states what it DECIDES (`incremental` + `upsert` |
| 48 | +vs `full` + `append`), because that contrast is the reason the pair exists. |
| 49 | + |
| 50 | +`SYNC_ARCHITECTURE.md`'s three examples now compile, and a compiler-API test |
| 51 | +(`etl-author-shape.test.ts`) compiles them verbatim on every run — import line included — |
| 52 | +so they cannot rot again silently. |
0 commit comments