Skip to content

refactor!: collapse decode into make and rename the schema members - #11

Merged
btravers merged 1 commit into
mainfrom
feat/one-entry-point
Aug 6, 2026
Merged

refactor!: collapse decode into make and rename the schema members#11
btravers merged 1 commit into
mainfrom
feat/one-entry-point

Conversation

@btravers

@btravers btravers commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The finding

decode and make are byte-identical on main:

static decode(raw)  { return parseEncoded(raw).…flatMap(recompute).flatMap(construct) }
static make(state)  { return parseEncoded(state).…flatMap(recompute).flatMap(construct) }

They converged in #10: fixing Copilot's healing bug meant make had to stop
validating against decoded, which made it the same function as decode. Two
public 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.

decode was also the name that implied an encode() #3 removed. Nothing
dangles now.

Renames

encoded/decoded were named after the operations, so with decode gone they
described nothing. They are renamed for what they are for, in the vocabulary
createInput/updateInput already established:

before after
Entity.decode(x) Entity.make(x)
Entity.encoded Entity.input
Entity.decoded Entity.output
Encoded<T> Input<T>
Decoded<T> Output<T>

Internally EncodedOf/DecodedOfInputOf/OutputOf, and the phantom
carriers __encoded/__decoded__input/__output.

I preferred this over wire/stored: it says what each schema is for rather
than how it was produced, and it does not introduce a second vocabulary beside
createInput/updateInput.

On hydrate

Considered 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. decode had the
mirror-image problem, leaning transport. make is plain, but it is the only
candidate that does not mislead about half its job.

Worth knowing

input and output differ only by the computed fields, and are identical when
an entity declares none. make accepts either, since computed keys are
re-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 Actions
has been in a major outage since 15:22 UTC with webhook delivery throttled.

Copilot AI lite review requested due to automatic review settings August 6, 2026 23:14
@btravers
btravers merged commit 26a8090 into main Aug 6, 2026
1 check passed
@btravers
btravers deleted the feat/one-entry-point branch August 6, 2026 23:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 through Entity.make(...) (including factories and instance).
  • Rename schema statics and exported helper types: encoded/decodedinput/output, Encoded/DecodedInput/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 make twice ("make, make and update") which looks like an accidental duplication; create is 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 make twice 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 make twice 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 though make is 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 thread README.md
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 thread README.md
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`;
Comment thread packages/entity/README.md
```

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 thread packages/entity/README.md
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`
Comment thread README.md
```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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants