Skip to content

Add schema-derived binary codec - #7366

Merged
tim-smart merged 66 commits into
mainfrom
agent/codex-engineer/58cacc24
Aug 24, 2026
Merged

Add schema-derived binary codec#7366
tim-smart merged 66 commits into
mainfrom
agent/codex-engineer/58cacc24

Conversation

@tim-smart

@tim-smart tim-smart commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add the schema-derived compact binary codec, streaming frame parser, and explicit field-id annotation
  • compile encoded-side layouts with evolution-safe struct fields, union variants, packed arrays and tuples, native declarations, generic declaration codecs, and cyclic-value rejection
  • export SchemaBinary from effect/unstable/encoding and cover framing, evolution, parser behavior, native values, declarations, recursion, parse options, and error surfaces
  • add fingerprinted SchemaBinary RPC envelope framing with evolution-friendly payload codecs, configurable 16 MiB frame limits, and raw-byte opaque holes
  • expand edge coverage for zero-width optionals, literal and symbol kinds, field ids, unknown fields, parser terminal states, native tag failures, future cause tags, JSON failures, and shared recursive DAGs
  • benchmark SchemaBinary, JSON, Msgpack, and Protobuf encode/decode throughput and payload size with equivalent Schema values
  • benchmark end-to-end RPC request, success-exit, and stream-chunk serialization with Msgpack and SchemaBinary

Verification

  • nix develop -c pnpm lint
  • nix develop -c pnpm check
  • nix develop -c pnpm circular
  • nix develop -c pnpm vitest run packages/effect/test/unstable/encoding packages/effect/test/rpc packages/effect/test/cluster
  • nix develop -c pnpm test --run --project effect test/unstable/encoding/SchemaBinary.test.ts
  • nix develop -c pnpm coverage --run --project effect test/unstable/encoding/SchemaBinary.test.ts --coverage.include=packages/effect/src/unstable/encoding/SchemaBinary.ts --coverage.reporter=text
  • nix develop -c pnpm --dir packages/effect exec node benchmark/schema/SchemaBinary.ts
  • nix develop -c pnpm --dir packages/effect exec node benchmark/rpc/RpcSerialization.ts

Closes EFF-772
Closes EFF-773
Closes EFF-779
Closes EFF-784
Closes EFF-816
Closes EFF-827
Closes EFF-831
Closes EFF-832

Closes EFF-834
Closes EFF-840

@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 57f8e22

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-slopcop effect-slopcop Bot added enhancement New feature or request 4.0 labels Aug 20, 2026
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 6.96 KB 6.96 KB 0.00 KB (0.00%)
batching.ts 9.81 KB 9.81 KB 0.00 KB (0.00%)
brand.ts 6.55 KB 6.55 KB 0.00 KB (0.00%)
cache.ts 10.73 KB 10.73 KB 0.00 KB (0.00%)
config.ts 21.49 KB 21.44 KB +0.06 KB (+0.26%)
differ.ts 20.32 KB 20.27 KB +0.05 KB (+0.25%)
http-client.ts 21.74 KB 21.74 KB 0.00 KB (0.00%)
logger.ts 10.91 KB 10.91 KB 0.00 KB (0.00%)
metric.ts 8.95 KB 8.95 KB 0.00 KB (0.00%)
optic.ts 6.71 KB 6.71 KB 0.00 KB (0.00%)
pubsub.ts 15.09 KB 15.09 KB 0.00 KB (0.00%)
queue.ts 11.77 KB 11.77 KB 0.00 KB (0.00%)
schedule.ts 10.84 KB 10.84 KB 0.00 KB (0.00%)
schema-binary.ts 39.06 KB 39.06 KB 0.00 KB (0.00%)
schema-class.ts 20.08 KB 20.02 KB +0.05 KB (+0.27%)
schema-fromJsonSchemaDocument.ts 30.37 KB 30.32 KB +0.05 KB (+0.15%)
schema-representation-roundtrip.ts 26.22 KB 26.17 KB +0.05 KB (+0.19%)
schema-string-transformation.ts 13.65 KB 13.65 KB 0.00 KB (0.00%)
schema-string.ts 11.15 KB 11.15 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.45 KB 15.45 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 22.00 KB 22.00 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.51 KB 24.45 KB +0.06 KB (+0.24%)
schema-toCodecJson.ts 19.28 KB 19.23 KB +0.05 KB (+0.28%)
schema-toEquivalence.ts 19.06 KB 19.06 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.92 KB 18.92 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 23.42 KB 23.37 KB +0.05 KB (+0.22%)
schema-toRepresentation.ts 19.54 KB 19.54 KB 0.00 KB (0.00%)
schema.ts 19.25 KB 19.25 KB 0.00 KB (0.00%)
stm.ts 12.72 KB 12.72 KB 0.00 KB (0.00%)
stream.ts 9.78 KB 9.78 KB 0.00 KB (0.00%)

@effect-bot
effect-bot changed the base branch from main to v4/next-minor August 20, 2026 06:22
@effect-slopcop effect-slopcop Bot removed the 4.0 label Aug 20, 2026
@IMax153
IMax153 changed the base branch from v4/next-minor to main August 20, 2026 12:47
Comment thread .changeset/schema-binary-codec.md Outdated
Comment thread .changeset/schema-binary-fingerprint-mode.md Outdated
Comment thread .changeset/schema-binary-varint-numbers.md Outdated
@effect-bot
effect-bot changed the base branch from main to v4/next-minor August 20, 2026 19:18
@effect-slopcop effect-slopcop Bot removed the 4.0 label Aug 20, 2026
@tim-smart
tim-smart force-pushed the agent/codex-engineer/58cacc24 branch from 87d2fa4 to 68df691 Compare August 21, 2026 00:41
Base automatically changed from v4/next-minor to main August 21, 2026 09:03
@tim-smart
tim-smart force-pushed the agent/codex-engineer/58cacc24 branch from fd1ef4f to 0778874 Compare August 21, 2026 09:06
Comment thread packages/effect/src/unstable/rpc/RpcSerialization.ts Outdated
tim-smart and others added 17 commits August 24, 2026 00:40
Pack schema field ids with wire kinds so scalar values no longer need per-field extents and booleans carry their value in the tag. Apply the same header design to index-signature record pairs, including interned keys.

Give compact decimals their own field kind, keep every kind skippable for schema evolution, and bump both envelopes to wire version 2.

Follow-up: row shape declarations can retain scalar wire kinds so reused rows can omit per-value extents.
Arrays of structs now use row runs in fingerprint mode, so repeated strings
and record keys intern across rows. A declared shape is the presence mask
over layout field order instead of a field id list, and positional structs
honor the handed-over key intern table.

The 200-row fingerprint payload drops from 19,454 to 7,741 bytes and its
decode rate matches the default mode.
A cleanup pass over the codec with no wire, API, or behavior changes; the
benchmark medians are unchanged within run noise and all 229 affected tests
pass.

Reuse
- The pooled-writer protocol lives once: encodeFrame and encodeFrames were
  copy-paste twins of the checkout/reset/abort/restore sequence and now share
  runFrames.
- The two hand-rolled WeakMap caches (compileMode, compileTarget) use
  Function.memoize.
- assignProperty imports the shared __proto__-safe helper from
  internal/record.ts instead of carrying a byte-for-byte copy.
- The local empty ParseOptions constant is SchemaAST.defaultParseOptions.

Simplification
- One registry maps native declaration ids to wire kind and exactness;
  toBinaryASTStep, astKind, and isExact read it instead of restating the id
  list, and compileDeclaration's fallback now fails loudly if a registry row
  has no layout constructor.
- The success-exit bypass predicate is defined once (isSuccessExit) instead of
  restated at five entry points.
- withTrustedDecode and passThrough share one bypassDecode factory; the codec
  pipe assembly shared by toCodec and toCodecDirect lives in assembleCodec.
- The required-field epilogue duplicated between decodeStruct and decodeRunRow
  is checkRequiredFields.
- Mode carries the fingerprint only as its byte form; the lo/hi words and
  their dummy defaults are gone.
- decodeInline was subsumed by decodeSlot; sentinel sets hash once during
  union compilation; the encoder's unreachable EMPTY wire case is gone; two
  test fixtures (Varying rows, the recursive Tree factory) are hoisted instead
  of repeated.

Efficiency
- decimalScale exits on the first out-of-range mantissa, which also rejects
  NaN and the infinities in one comparison instead of running all eight
  scales.
- The decimal number-run write pass reuses the scales found during
  classification instead of rescanning each non-integer.
- Literal sets larger than four values check membership in a Set.
- The channel helpers derive their schema-dependent codecs once per channel
  instead of once per run, and decodeRunRow only records declared slots when
  the plan captures shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Layout-level index-signature shape cache and an unrolled uvarint fast path, from Codex Engineer.
Fixed-layout fast path for required-only struct runs and an unrolled ASCII string write, from Codex Engineer; the shared per-field write is factored out so the two row loops keep one copy of it. Benchmark numbers refreshed.
@tim-smart
tim-smart force-pushed the agent/codex-engineer/58cacc24 branch from 8c65fcc to f0536ef Compare August 24, 2026 00:42
tim-smart and others added 9 commits August 24, 2026 00:47
SchemaBinary.encoder pairs with SchemaBinary.parser; dictionary: true makes them share one string table per string leaf across frames. RPC serialization enables it for envelopes, halving a typical request frame. The RPC benchmark now walks a stream in frame order instead of replaying one frame.
The binary layer already rejects every value for a never layout, already accepts every JSON value an unknown or any schema can hold, and validates a recursive schema in full on decode. Encoding a recursive schema keeps the pass because the cycle walk lives there.
The bypass hands the input straight back, which the shared exit says without allocating one per call. Benchmark numbers refreshed, including the adversarial raw-serializer tables.
One own-property enumeration and a lazily compiled name map replace per-field hasOwn discovery in row runs. From Codex Engineer.
@tim-smart
tim-smart enabled auto-merge (squash) August 24, 2026 04:34
@tim-smart
tim-smart merged commit 0dd7825 into main Aug 24, 2026
18 checks passed
@tim-smart
tim-smart deleted the agent/codex-engineer/58cacc24 branch August 24, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants