|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(spec): name the parsed state `XParsed` on every schema that has one (ADR-0122, #5551) |
| 6 | + |
| 7 | +A Zod schema denotes two types — `z.input` (what an author writes: defaulted keys |
| 8 | +optional, pre-transform) and `z.infer` (what `.parse()` returns) — and `packages/spec` |
| 9 | +has been naming them two different ways with nothing written down about which is which. |
| 10 | +Measurement on `origin/main`: **1384** bare aliases mean the parsed state, **86** mean |
| 11 | +the author state, and three separate first-hand sources each described the 8-file |
| 12 | +minority as "the house convention". No ADR recorded either spelling. |
| 13 | + |
| 14 | +**[ADR-0122](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0122-schema-type-alias-naming-convention.md) |
| 15 | +settles it: the bare name `X` is the AUTHOR state, `XParsed` is the PARSED state.** The |
| 16 | +deciding argument is the keystroke every author writes first — `const c: Connector = { … }` |
| 17 | +— which should be correct by default in every domain, without knowing which file you are in. |
| 18 | + |
| 19 | +**This release is phase 1, and it is purely additive. Nothing is renamed or removed; |
| 20 | +no existing annotation stops compiling.** It declares `XParsed` for the **657** aliases |
| 21 | +whose schema genuinely has two distinct shapes, so that every consumer whose meaning |
| 22 | +phase 2 will change already has a name to move to: |
| 23 | + |
| 24 | +```ts |
| 25 | +// before — one name, meaning the parsed state |
| 26 | +export type Connector = z.infer< typeof ConnectorSchema >; |
| 27 | +export type ConnectorInput = z.input< typeof ConnectorSchema >; |
| 28 | + |
| 29 | +// after — the parsed state also has a name that will keep meaning it |
| 30 | +export type Connector = z.infer< typeof ConnectorSchema >; |
| 31 | +export type ConnectorParsed = z.infer< typeof ConnectorSchema >; // new |
| 32 | +export type ConnectorInput = z.input< typeof ConnectorSchema >; // unchanged |
| 33 | +``` |
| 34 | + |
| 35 | +Schemas whose `z.input` and `z.infer` are the *same* type (enums, plain unions, objects |
| 36 | +with no defaults or transforms anywhere in their tree) deliberately get **no** `XParsed` |
| 37 | +— a permanent synonym is a name you can only pick wrongly. All 718 of them are pinned |
| 38 | +with compile-time assertions so the exemption cannot rot silently when one later gains |
| 39 | +a `.default()`. |
| 40 | + |
| 41 | +One name to note if you are upgrading across protocol 17: `FieldMapping` does **not** |
| 42 | +gain a `FieldMappingParsed`. #5552 retired `FieldMapping.transform` and the whole |
| 43 | +`FieldMappingTransform` union in the same release, and that key was the only reason the |
| 44 | +schema had two shapes — so it is now isomorphic, and under this convention it correctly |
| 45 | +keeps exactly one name. |
| 46 | + |
| 47 | +**What to do now (optional, and cheap).** If you hold the result of a `.parse()` — or of |
| 48 | +a `defineX()` factory, which returns it — move that annotation to `XParsed`: |
| 49 | + |
| 50 | +```ts |
| 51 | +-const c: Connector = ConnectorSchema.parse(raw); |
| 52 | ++const c: ConnectorParsed = ConnectorSchema.parse(raw); |
| 53 | +``` |
| 54 | + |
| 55 | +Annotations on values you *write* need no change now and will be correct after phase 2. |
| 56 | +Doing nothing is also fine until then. |
| 57 | + |
| 58 | +**What comes next.** Phase 2 flips the bare names to `z.input` and ships in a major, with |
| 59 | +its own changeset and migration notes. `XInput` aliases are untouched by this release and |
| 60 | +their fate is decided then. |
0 commit comments