Schema: a public way to derive a canonical codec, as `toCodecJson` and `toCodecStringTree` do
メンテナーはふだん 1 日以内に返信
@effect-bot がすでに取り組んでいます。
2026年11月7日 から。
評価
この issue はまだ評価されていません。
説明
What is the problem this feature would solve?
This is one focused use case split out of #8690, as requested there. It is related to ask 7 ("A public structural AST map/rebuild"), but it covers more.
We write a Firestore integration for Effect. Firestore stores a fixed set of values: null, booleans, numbers, strings, timestamps, bytes, geo points, document references, arrays and maps. That makes it a canonical format in the same way JSON is one. So we derive toCodecFirestore(schema) the way Effect derives toCodecJson(schema):
- A declaration is looked up by a
toCodecFirestoreannotation, thentoCodecJson, thentoCodec, the same fallbacktoCodecStringTreeuses. Date,DateTimeUtcandUint8Arraymap to Firestore's own timestamp and bytes types instead of strings.Unknown,AnyandObjectKeywordaccept only values Firestore can store.- Keywords, objects, unions, arrays and suspends are handled as
toCodecJsonhandles them.
The step function is ours, but the machinery around it is only available internally. toCodecFirestore is a copy of the internal makeCodecDerivation in internal/schema/toCodec.ts plus our own step function. Building it needs these exports, all of which exist at runtime but are @internal and stripped from the .d.ts:
| Internal API | What we use it for |
|---|---|
SchemaAST.applyToSelfOrLastLinkEncodingIdempotent |
Run the step on a node or on its last link's target, memoized |
SchemaAST.replaceEncoding |
Give a declaration or keyword its canonical link |
SchemaAST.mapLink |
Recurse into the target of a declaration's toCodec* link |
SchemaAST.replaceContextLastLink |
Move the context, minus the constructor default, onto the last link |
SchemaAST.parameterFromString |
Read object keys back from strings |
ast.recur(f, onKey?) |
Recurse into Objects, Arrays, Union and Suspend |
ast.toCodecJson() / ast.toCodecStringTree() on keyword nodes |
Reuse Effect's canonical form for undefined, literals, bigint and symbols |
Today we type these by hand behind a cast and pin effect to an exact version. This is fragile:
- A patch release can change or remove any of them without notice. That is fair, since they are internal.
- Our copy drifts from Effect's.
makeCodecDerivationnow derives each body once per context owner (getContextOwner+replaceContext) so that shared schema references stay shared. Our copy predates that change and doesn't get it. - Every library that wants its own format (a database's value model, a wire format, a config tree) has to reimplement the same few dozen lines against internals.
What is the feature you are proposing to solve the problem?
Make the derivation machinery public, so that a library supplies only the step function and Effect owns the rest. Roughly:
// SchemaAST
export function makeCodecDerivation(
step: (ast: AST, recur: (ast: AST) => AST) => AST,
options?: { readonly stopAt?: (link: Link) => boolean }
): (ast: AST) => AST
The step function also needs a few public building blocks:
replaceEncoding(ast, encoding)andmapLink(link, f)parameterFromString- A typed
ast.recur(f, onKey?), or aSchemaAST.recur(ast, f, onKey?)function. This overlaps with ask 7 of #8690. - Public access to the canonical JSON and StringTree forms of a keyword node, for example
SchemaAST.keywordToCodecJson(ast)andSchemaAST.keywordToCodecStringTree(ast), or simply publictoCodecJson()/toCodecStringTree()methods
With these, toCodecFirestore comes down to:
import { Schema, SchemaAST } from "effect"
const toCodecFirestoreAST = SchemaAST.makeCodecDerivation((ast, recur) => {
switch (ast._tag) {
case "Declaration": /* look up toCodecFirestore ?? toCodecJson ?? toCodec, mapLink(link, recur) */
case "Unknown":
case "Any":
case "ObjectKeyword":
return SchemaAST.replaceEncoding(ast, [unknownToFirestore])
case "Objects":
return SchemaAST.recur(ast, recur, SchemaAST.parameterFromString)
// …
}
return ast
})
export const toCodecFirestore = <S extends Schema.Constraint>(schema: S) =>
Schema.make(toCodecFirestoreAST(schema.ast), { schema })
Effect's own toCodecJson, toCodecStringTree and toCodecGoJson would be built on the same public API, so the API gets tested by Effect itself and keeps up with fixes such as the context-owner sharing.
A smaller first step would also help us: publishing the individual helpers listed above, even without makeCodecDerivation.
What alternatives have you considered?
- Use
toCodecJsonand convert JSON to Firestore values afterwards. This loses Firestore's own types. ADatebecomes a string instead of a timestamp, and bytes become base64. Reading back can't tell a timestamp-as-string from a real string. - Build the codec through the public node constructors. This needs a hand-written rebuild of every node kind. As #8690 ask 7 notes, it breaks whenever a constructor gains a field (
encodingCheckswas added during the RCs). It also still can't reachparameterFromStringor the keywords' canonical forms. - Keep the internal helpers behind a cast with
effectpinned exactly (what we do now). It works, but consumers can't take Effect patch releases without us re-checking the internals first.
- 主要言語
- TypeScript
- スター
- 16.7k
- フォーク
- 808
- 平均マージ
- 12時間 17分
- マージ済み PR(30日)
- 805
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Effect-TS/effect のほかの issue
-
From Discord: Return Stream.empty for HTTP requests with an empty body対応中かも @pujitha24 が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 30/100
メンテナーはふだん 1 日以内に返信
-
sql-pg: scoped connection shutdown retains a socket when the peer withholds FIN対応中かも @effect-bot が今日担当しました。 オープン
Effect-TS/effect#10458 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
McpServer.layerHttp: optional legacy GET SSE for idle list-change notifications対応中かも @effect-bot が 1 日前に担当しました。 オープン
Effect-TS/effect#10436 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
SqlClient: a fiber forked inside a transaction keeps using its connection after the transaction ends対応中かも @effect-bot が 1 日前に担当しました。 オープンbug
Effect-TS/effect#10431 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
CLI completions stop offering repeatable flags after their first use対応中かも @effect-bot が 1 日前に担当しました。 オープン
Effect-TS/effect#10427 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
Effect-TS/effect の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
backnotprop/plannotator#2914 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
microsoft/vscode-containers#655 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
dsh-tauri-desk/deepseek-harness-desktop#1477 · コメント 1 件 ·