refactor!: collapse decode into make and rename the schema members - #11
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR removes the redundant decode entry point by folding its behavior into make, and renames the schema/statics and helper types to reflect purpose (input/output) rather than historical operations (encoded/decoded). It updates the public API surface (EntityStatic), implementation (entity.ts, instance.ts), tests, docs, and adds a changeset for the breaking rename/removal.
Changes:
- Remove
Entity.decode(...)and route all construction throughEntity.make(...)(including factories andinstance). - Rename schema statics and exported helper types:
encoded/decoded→input/output,Encoded/Decoded→Input/Output(+ corresponding internal type aliases). - Update docs/specs and add a changeset describing the breaking change.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates public docs for the single make entry point and input/output naming. |
| packages/entity/src/union.spec.ts | Adjusts union-contract tests to use output and make. |
| packages/entity/src/types.ts | Renames core inferred types and updates EntityStatic to remove decode and expose input/output. |
| packages/entity/src/types.test-d.ts | Updates type-level tests to match InputOf/OutputOf renames. |
| packages/entity/src/instance.ts | Switches instance to transform via make and the input schema. |
| packages/entity/src/instance.spec.ts | Updates instance behavior tests and wording from decode→make. |
| packages/entity/src/index.ts | Re-exports the renamed helper types (Input, Output). |
| packages/entity/src/freeze.ts | Updates comment terminology to match renamed shapes. |
| packages/entity/src/equality.spec.ts | Updates equality tests to construct via make. |
| packages/entity/src/entity.ts | Removes decode, renames schemas/statics to input/output, and routes factory creation through make. |
| packages/entity/src/entity.test-d.ts | Updates compile-time surface tests for the renamed APIs/types. |
| packages/entity/src/entity.spec.ts | Updates runtime behavior tests to use make and renamed schema statics. |
| packages/entity/src/contract.spec.ts | Updates JSON-schema/contract tests to use input/output. |
| packages/entity/src/computed.spec.ts | Updates computed-field tests to use make and renamed schema statics. |
| packages/entity/README.md | Updates package-level README for the renamed statics/types and make-only API. |
| CLAUDE.md | Updates repo guidance to reflect input/output and make being the construction path. |
| .changeset/one-entry-point.md | Adds release notes for removing decode and renaming schema/type members. |
Suppressed comments (4)
README.md:259
- The computed-fields section lists
maketwice ("make,makeandupdate") which looks like an accidental duplication;createis the other construction path that re-derives computed fields.
**re-derived on every construction** — `make`, `make` and `update` alike:
README.md:367
- This sentence lists
maketwice in the entry-point list; it should mention each entry point once.
`update`, `make` and `make`. Because data is _deeply_ immutable once
README.md:520
- Sealed construction section lists
maketwice in the list of allowed construction paths; it should only appear once.
instance is built through `create`/`update`/`make`/`make`, which means
packages/entity/README.md:74
- The
make(state)row still describes only "already-stored state" even thoughmakeis now the sole entry point for both stored rows and untrusted payloads. The table should reflect the broader purpose to avoid misleading consumers.
| `make(state)` | method | already-stored state → entity, for row mappers and event folds |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
22
to
+24
| **[Standard Schema](https://standardschema.dev)**, with entry points named for | ||
| the use case they serve (`create`, `update`, `make`, `decode`) instead of one | ||
| generic `decode`. | ||
| the use case they serve (`create`, `update`, `make`, `make`) instead of one | ||
| generic `make`. |
Comment on lines
55
to
58
| to a JSON Schema converter, plus `Organization.instance` for decoding | ||
| straight to a class instance; | ||
| - **behaviour** — the class body (`greeting` above) plus built-in | ||
| `update`/`encode`/`toJSON`/`equals`; |
| ``` | ||
|
|
||
| Every fallible entry point (`decode`, `make`, `create`, `update`) returns an | ||
| Every fallible entry point (`make`, `make`, `create`, `update`) returns an |
| @@ -62,15 +62,15 @@ test("a branded field survives DeepReadonly with its brand intact", () => { | |||
| }); | |||
|
|
|||
| test("toJSON() returns the plain, mutable decoded shape", () => { | |||
Comment on lines
+127
to
128
| A computed field is **re-derived on every construction** — `make`, `make` and | ||
| `update` alike — so it cannot drift from the data it derives from, and `make` |
| ```ts | ||
| Person.make(person.toJSON()); // ✓ the natural pairing | ||
| Person.decode(person.toJSON()); // ✓ also fine — computed keys are re-derived | ||
| Person.make(person.toJSON()); // ✓ also fine — computed keys are re-derived |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The finding
decodeandmakeare byte-identical onmain:They converged in #10: fixing Copilot's healing bug meant
makehad to stopvalidating against
decoded, which made it the same function asdecode. Twopublic names for one operation.
Rehydrating a database row and validating an untrusted import differ in where
the data came from, not in what has to happen to it — parse, re-derive, check
invariants, construct. So there is one entry point now:
make.decodewas also the name that implied anencode()#3 removed. Nothingdangles now.
Renames
encoded/decodedwere named after the operations, so withdecodegone theydescribed nothing. They are renamed for what they are for, in the vocabulary
createInput/updateInputalready established:Entity.decode(x)Entity.make(x)Entity.encodedEntity.inputEntity.decodedEntity.outputEncoded<T>Input<T>Decoded<T>Output<T>Internally
EncodedOf/DecodedOf→InputOf/OutputOf, and the phantomcarriers
__encoded/__decoded→__input/__output.I preferred this over
wire/stored: it says what each schema is for ratherthan how it was produced, and it does not introduce a second vocabulary beside
createInput/updateInput.On
hydrateConsidered and rejected for the surviving entry point. It is right for a
database row and wrong for a replayed integration event or an untrusted import,
which the same function serves — and it now reads as SSR.
decodehad themirror-image problem, leaning transport.
makeis plain, but it is the onlycandidate that does not mislead about half its job.
Worth knowing
inputandoutputdiffer only by the computed fields, and are identical whenan entity declares none.
makeaccepts either, since computed keys arere-derived and unknown keys ignored. That the split is thin is now visible in
the names, which seems better than hiding it.
Gate
format --check,lint,typecheck(both passes),test(94, 9 files),knip,build— all green locally. CI still is not triggering; GitHub Actionshas been in a major outage since 15:22 UTC with webhook delivery throttled.