@dxos/effect-zod 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +155 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/node-esm/index.mjs +155 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/types/src/effect-to-zod.d.ts +9 -0
- package/dist/types/src/effect-to-zod.d.ts.map +1 -0
- package/dist/types/src/effect-to-zod.test.d.ts +2 -0
- package/dist/types/src/effect-to-zod.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +39 -0
- package/src/effect-to-zod.test.ts +140 -0
- package/src/effect-to-zod.ts +248 -0
- package/src/index.ts +5 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import "@dxos/node-std/globals";
|
|
2
|
+
|
|
3
|
+
// src/effect-to-zod.ts
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
var DescriptionAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Description");
|
|
6
|
+
var JSONSchemaAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONSchema");
|
|
7
|
+
var effectFieldsToZod = (schema) => {
|
|
8
|
+
const out = {};
|
|
9
|
+
for (const [name, prop] of Object.entries(schema.fields)) {
|
|
10
|
+
try {
|
|
11
|
+
out[name] = propToZod(prop.ast);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
throw new Error(`effectFieldsToZod: failed to convert field "${name}": ${err.message}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
};
|
|
18
|
+
var propToZod = (ast) => {
|
|
19
|
+
if (ast._tag === "PropertySignatureDeclaration") {
|
|
20
|
+
const description = readDescription(ast);
|
|
21
|
+
const isOptional = Boolean(ast.isOptional);
|
|
22
|
+
const innerAst = unwrapOptionalUnion(ast.type, isOptional);
|
|
23
|
+
let zod = astToZod(innerAst);
|
|
24
|
+
if (isOptional) {
|
|
25
|
+
zod = zod.optional();
|
|
26
|
+
}
|
|
27
|
+
if (description !== void 0) {
|
|
28
|
+
zod = zod.describe(description);
|
|
29
|
+
}
|
|
30
|
+
return zod;
|
|
31
|
+
}
|
|
32
|
+
return astToZod(ast);
|
|
33
|
+
};
|
|
34
|
+
var unwrapOptionalUnion = (ast, isOptional) => {
|
|
35
|
+
if (!isOptional || ast._tag !== "Union") {
|
|
36
|
+
return ast;
|
|
37
|
+
}
|
|
38
|
+
const types = ast.types.filter((t) => t._tag !== "UndefinedKeyword");
|
|
39
|
+
if (types.length === 1) {
|
|
40
|
+
return types[0];
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...ast,
|
|
44
|
+
types
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var astToZod = (ast) => {
|
|
48
|
+
let zod;
|
|
49
|
+
switch (ast._tag) {
|
|
50
|
+
case "StringKeyword":
|
|
51
|
+
zod = z.string();
|
|
52
|
+
break;
|
|
53
|
+
case "NumberKeyword":
|
|
54
|
+
zod = z.number();
|
|
55
|
+
break;
|
|
56
|
+
case "BooleanKeyword":
|
|
57
|
+
zod = z.boolean();
|
|
58
|
+
break;
|
|
59
|
+
case "Literal":
|
|
60
|
+
zod = z.literal(ast.literal);
|
|
61
|
+
break;
|
|
62
|
+
case "Union": {
|
|
63
|
+
const types = ast.types;
|
|
64
|
+
const allStringLiteral = types.every((t) => t._tag === "Literal" && typeof t.literal === "string");
|
|
65
|
+
if (!allStringLiteral) {
|
|
66
|
+
throw new Error(`unsupported Union \u2014 only enum-of-string-literals supported, got branches: ${types.map((t) => t._tag).join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
const values = types.map((t) => t.literal);
|
|
69
|
+
zod = z.enum(values);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "TupleType": {
|
|
73
|
+
const tuple = ast;
|
|
74
|
+
if (tuple.elements && tuple.elements.length > 0) {
|
|
75
|
+
throw new Error("fixed-length tuples are not supported \u2014 use Schema.Array(X)");
|
|
76
|
+
}
|
|
77
|
+
const elem = tuple.rest?.[0]?.type;
|
|
78
|
+
if (!elem) {
|
|
79
|
+
throw new Error("TupleType without rest element \u2014 Schema.Array(X) is the only supported array form");
|
|
80
|
+
}
|
|
81
|
+
zod = z.array(astToZod(elem));
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case "Refinement": {
|
|
85
|
+
const { base, jsonSchemas } = collectRefinements(ast);
|
|
86
|
+
let z0 = astToZod(base);
|
|
87
|
+
for (const js of jsonSchemas) {
|
|
88
|
+
z0 = applyJsonSchemaRefinement(z0, js);
|
|
89
|
+
}
|
|
90
|
+
zod = z0;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`unsupported Effect Schema AST node: ${ast._tag}`);
|
|
95
|
+
}
|
|
96
|
+
const description = readDescription(ast);
|
|
97
|
+
if (description !== void 0) {
|
|
98
|
+
zod = zod.describe(description);
|
|
99
|
+
}
|
|
100
|
+
return zod;
|
|
101
|
+
};
|
|
102
|
+
var collectRefinements = (ast) => {
|
|
103
|
+
const jsonSchemas = [];
|
|
104
|
+
let cursor = ast;
|
|
105
|
+
while (cursor._tag === "Refinement") {
|
|
106
|
+
const js = cursor.annotations?.[JSONSchemaAnnotationId];
|
|
107
|
+
if (typeof js === "object" && js !== null) {
|
|
108
|
+
jsonSchemas.push(js);
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error("Refinement is missing a JSONSchema annotation \u2014 only Effect stdlib refinements (int, positive, lessThanOrEqualTo, etc.) are currently supported");
|
|
111
|
+
}
|
|
112
|
+
cursor = cursor.from;
|
|
113
|
+
}
|
|
114
|
+
jsonSchemas.reverse();
|
|
115
|
+
return {
|
|
116
|
+
base: cursor,
|
|
117
|
+
jsonSchemas
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
var applyJsonSchemaRefinement = (zod, js) => {
|
|
121
|
+
let z0 = zod;
|
|
122
|
+
let touched = false;
|
|
123
|
+
if (js.type === "integer") {
|
|
124
|
+
z0 = z0.int();
|
|
125
|
+
touched = true;
|
|
126
|
+
}
|
|
127
|
+
if (typeof js.exclusiveMinimum === "number" && js.exclusiveMinimum === 0) {
|
|
128
|
+
z0 = z0.positive();
|
|
129
|
+
touched = true;
|
|
130
|
+
}
|
|
131
|
+
if (typeof js.maximum === "number") {
|
|
132
|
+
z0 = z0.max(js.maximum);
|
|
133
|
+
touched = true;
|
|
134
|
+
}
|
|
135
|
+
if (typeof js.minimum === "number") {
|
|
136
|
+
z0 = z0.min(js.minimum);
|
|
137
|
+
touched = true;
|
|
138
|
+
}
|
|
139
|
+
if (!touched) {
|
|
140
|
+
throw new Error(`unsupported JSONSchema refinement fragment: ${JSON.stringify(js)}`);
|
|
141
|
+
}
|
|
142
|
+
return z0;
|
|
143
|
+
};
|
|
144
|
+
var readDescription = (ast) => {
|
|
145
|
+
const annotations = ast.annotations;
|
|
146
|
+
if (!annotations) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
const value = annotations[DescriptionAnnotationId];
|
|
150
|
+
return typeof value === "string" ? value : void 0;
|
|
151
|
+
};
|
|
152
|
+
export {
|
|
153
|
+
effectFieldsToZod
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/effect-to-zod.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\n// Effect Schema → Zod converter, scoped to the patterns we use in MCP tool\n// inputs. The MCP SDK requires zod schemas for `inputSchema`, but we want to\n// author tool inputs in Effect Schema so:\n//\n// 1. The same definitions can be consumed by `react-ui-form` (which renders\n// forms from Effect Schema directly).\n// 2. We get Effect's annotation system (Description, Title, JSONSchema)\n// everywhere, plus refinements that compose cleanly with the rest of\n// the codebase.\n//\n// What's supported (anything outside this list throws at startup with a clear\n// message — strict denylist beats silent miscompilation):\n//\n// Schema.String → z.string()\n// Schema.Number → z.number()\n// Schema.Boolean → z.boolean()\n// Schema.Literal('a','b') → z.enum(['a','b'])\n// Schema.Array(x) → z.array(zodOf(x))\n// Schema.optional(x) → .optional()\n// Schema.int() → .int() (via JSONSchema annotation)\n// Schema.positive() → .positive() (via JSONSchema annotation)\n// Schema.lessThanOrEqualTo(n) → .max(n) (via JSONSchema annotation)\n// description annotation → .describe(...)\n//\n// We read refinements off the JSONSchema annotation Effect attaches to its\n// stdlib refinements, NOT off SchemaId symbols — that gives us a stable\n// integration point that doesn't break across Effect minor versions.\n\nimport * as Schema from 'effect/Schema';\nimport { z } from 'zod';\n\nconst DescriptionAnnotationId = Symbol.for('effect/annotation/Description');\nconst JSONSchemaAnnotationId = Symbol.for('effect/annotation/JSONSchema');\n\n/**\n * Convert the fields of an Effect `Schema.Struct(...)` into the\n * `Record<string, z.ZodTypeAny>` shape the MCP SDK's `registerTool` expects\n * for `inputSchema`.\n */\nexport const effectFieldsToZod = <Fields extends Schema.Struct.Fields>(\n schema: Schema.Struct<Fields>,\n): Record<keyof Fields & string, z.ZodTypeAny> => {\n const out: Record<string, z.ZodTypeAny> = {};\n for (const [name, prop] of Object.entries(schema.fields)) {\n try {\n out[name] = propToZod((prop as { ast: AnyAst }).ast);\n } catch (err) {\n throw new Error(`effectFieldsToZod: failed to convert field \"${name}\": ${(err as Error).message}`);\n }\n }\n return out as Record<keyof Fields & string, z.ZodTypeAny>;\n};\n\n/**\n * `Schema.PropertySignature` (the AST node a struct's `.fields[name].ast` is)\n * isn't part of the main `SchemaAST.AST` union — it's a separate hierarchy.\n * We use a structural type here that matches both shapes we encounter:\n *\n * - struct field signatures: `{ _tag: 'PropertySignatureDeclaration', type, isOptional, ... }`\n * - bare types (required fields, walked recursively): everything in `SchemaAST.AST`\n *\n * Treating `_tag` as a free string and downcasting selectively lets us handle\n * both without reaching for `as unknown as never` workarounds.\n */\ntype AnyAst = { _tag: string; annotations?: Record<symbol, unknown> } & Record<string, unknown>;\n\n/**\n * Convert one struct field's AST. Property signatures wrap the actual schema\n * AST with optional/readonly metadata; required fields are the AST directly.\n */\nconst propToZod = (ast: AnyAst): z.ZodTypeAny => {\n if (ast._tag === 'PropertySignatureDeclaration') {\n // `Schema.optional(X)` produces a PropertySignatureDeclaration whose `type`\n // is `Union(X, UndefinedKeyword)`. Peel UndefinedKeyword before recursing\n // so the converter operates on the user-facing type, then mark optional.\n const description = readDescription(ast);\n const isOptional = Boolean(ast.isOptional);\n const innerAst = unwrapOptionalUnion(ast.type as AnyAst, isOptional);\n let zod = astToZod(innerAst);\n if (isOptional) {\n zod = zod.optional();\n }\n if (description !== undefined) {\n zod = zod.describe(description);\n }\n return zod;\n }\n return astToZod(ast);\n};\n\n/**\n * Peel `UndefinedKeyword` from an optional field's union. Effect models\n * `optional(X)` as `Union(X, UndefinedKeyword)`; if that's what we have AND\n * the prop is optional, return X. Otherwise pass through unchanged.\n */\nconst unwrapOptionalUnion = (ast: AnyAst, isOptional: boolean): AnyAst => {\n if (!isOptional || ast._tag !== 'Union') {\n return ast;\n }\n const types = (ast.types as AnyAst[]).filter((t) => t._tag !== 'UndefinedKeyword');\n if (types.length === 1) {\n return types[0];\n }\n // Multi-branch union after stripping undefined — preserve as a Union; the\n // top-level switch handles literal-only unions (enums). Anything else\n // throws with a clear message.\n return { ...ast, types } as AnyAst;\n};\n\nconst astToZod = (ast: AnyAst): z.ZodTypeAny => {\n let zod: z.ZodTypeAny;\n switch (ast._tag) {\n case 'StringKeyword':\n zod = z.string();\n break;\n case 'NumberKeyword':\n zod = z.number();\n break;\n case 'BooleanKeyword':\n zod = z.boolean();\n break;\n case 'Literal':\n // `z.literal` accepts string | number | boolean | null. Effect's literal\n // value is already constrained to those by Schema.Literal's signature.\n zod = z.literal(ast.literal as string | number | boolean | null);\n break;\n case 'Union': {\n // Only support unions where every branch is a string literal — that's\n // what `Schema.Literal('a', 'b', 'c')` produces, and it maps directly\n // to `z.enum`. Other unions (mixed types, refinements) aren't currently\n // used in our tool inputs and would need a richer conversion.\n const types = ast.types as AnyAst[];\n const allStringLiteral = types.every((t) => t._tag === 'Literal' && typeof t.literal === 'string');\n if (!allStringLiteral) {\n throw new Error(\n `unsupported Union — only enum-of-string-literals supported, got branches: ${types.map((t) => t._tag).join(', ')}`,\n );\n }\n const values = types.map((t) => t.literal as string) as [string, ...string[]];\n zod = z.enum(values);\n break;\n }\n case 'TupleType': {\n // `Schema.Array(X)` produces a TupleType with a single rest element of\n // type X. Fixed tuples (`Schema.Tuple(...)`) aren't currently used.\n const tuple = ast as { rest?: ReadonlyArray<{ type: AnyAst }>; elements?: ReadonlyArray<unknown> };\n if (tuple.elements && tuple.elements.length > 0) {\n throw new Error('fixed-length tuples are not supported — use Schema.Array(X)');\n }\n const elem = tuple.rest?.[0]?.type;\n if (!elem) {\n throw new Error('TupleType without rest element — Schema.Array(X) is the only supported array form');\n }\n zod = z.array(astToZod(elem));\n break;\n }\n case 'Refinement': {\n // Walk the refinement chain down to the base type, collecting JSONSchema\n // annotations along the way. Apply each refinement's Zod equivalent on\n // top of the base. Order is deterministic: int → positive → max, so we\n // never trigger Zod's \"method must come after .int()\" sequencing rule.\n const { base, jsonSchemas } = collectRefinements(ast);\n let z0 = astToZod(base);\n for (const js of jsonSchemas) {\n z0 = applyJsonSchemaRefinement(z0, js);\n }\n zod = z0;\n break;\n }\n default:\n throw new Error(`unsupported Effect Schema AST node: ${ast._tag}`);\n }\n\n // Pass through Description annotation. This includes Effect's stdlib\n // defaults (\"a string\", \"a positive number\") if the user didn't override —\n // tool authors should always supply their own description for LLM trigger\n // accuracy, but we don't enforce that here.\n const description = readDescription(ast);\n if (description !== undefined) {\n zod = zod.describe(description);\n }\n return zod;\n};\n\nconst collectRefinements = (ast: AnyAst): { base: AnyAst; jsonSchemas: Array<Record<string, unknown>> } => {\n const jsonSchemas: Array<Record<string, unknown>> = [];\n let cursor: AnyAst = ast;\n while (cursor._tag === 'Refinement') {\n const js = cursor.annotations?.[JSONSchemaAnnotationId];\n if (typeof js === 'object' && js !== null) {\n jsonSchemas.push(js as Record<string, unknown>);\n } else {\n throw new Error(\n 'Refinement is missing a JSONSchema annotation — only Effect stdlib refinements (int, positive, lessThanOrEqualTo, etc.) are currently supported',\n );\n }\n cursor = cursor.from as AnyAst;\n }\n // Innermost refinements were pushed first; reverse so outer refinements\n // (e.g. `lessThanOrEqualTo`) apply LAST, after `.int().positive()` etc.\n jsonSchemas.reverse();\n return { base: cursor, jsonSchemas };\n};\n\n/**\n * Map a JSON Schema fragment from one Effect refinement to a Zod method on a\n * `z.number()`. We only translate the shapes Effect emits for its stdlib\n * refinements — anything else throws so the converter doesn't silently\n * generate an under-constrained Zod schema.\n */\nconst applyJsonSchemaRefinement = (zod: z.ZodTypeAny, js: Record<string, unknown>): z.ZodTypeAny => {\n // Only z.number() supports the methods we need. Defensive cast.\n let z0 = zod as z.ZodNumber;\n let touched = false;\n if (js.type === 'integer') {\n z0 = z0.int();\n touched = true;\n }\n if (typeof js.exclusiveMinimum === 'number' && js.exclusiveMinimum === 0) {\n z0 = z0.positive();\n touched = true;\n }\n if (typeof js.maximum === 'number') {\n z0 = z0.max(js.maximum);\n touched = true;\n }\n if (typeof js.minimum === 'number') {\n z0 = z0.min(js.minimum);\n touched = true;\n }\n if (!touched) {\n throw new Error(`unsupported JSONSchema refinement fragment: ${JSON.stringify(js)}`);\n }\n return z0;\n};\n\nconst readDescription = (ast: AnyAst): string | undefined => {\n const annotations = ast.annotations;\n if (!annotations) {\n return undefined;\n }\n const value = annotations[DescriptionAnnotationId];\n return typeof value === 'string' ? value : undefined;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["z", "DescriptionAnnotationId", "Symbol", "for", "JSONSchemaAnnotationId", "effectFieldsToZod", "schema", "out", "name", "prop", "Object", "entries", "fields", "propToZod", "ast", "err", "Error", "message", "_tag", "description", "readDescription", "isOptional", "Boolean", "innerAst", "unwrapOptionalUnion", "type", "zod", "astToZod", "optional", "undefined", "describe", "types", "filter", "t", "length", "string", "number", "boolean", "literal", "allStringLiteral", "every", "map", "join", "values", "enum", "tuple", "elements", "elem", "rest", "array", "base", "jsonSchemas", "collectRefinements", "z0", "js", "applyJsonSchemaRefinement", "cursor", "annotations", "push", "from", "reverse", "touched", "int", "exclusiveMinimum", "positive", "maximum", "max", "minimum", "min", "JSON", "stringify", "value"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"src/effect-to-zod.ts":{"bytes":29727,"imports":[{"path":"zod","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":481,"imports":[{"path":"src/effect-to-zod.ts","kind":"import-statement","original":"./effect-to-zod"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":14435},"dist/lib/browser/index.mjs":{"imports":[{"path":"zod","kind":"import-statement","external":true}],"exports":["effectFieldsToZod"],"entryPoint":"src/index.ts","inputs":{"src/effect-to-zod.ts":{"bytesInOutput":4314},"src/index.ts":{"bytesInOutput":0}},"bytes":4439}}}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/effect-to-zod.ts
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
var DescriptionAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Description");
|
|
6
|
+
var JSONSchemaAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONSchema");
|
|
7
|
+
var effectFieldsToZod = (schema) => {
|
|
8
|
+
const out = {};
|
|
9
|
+
for (const [name, prop] of Object.entries(schema.fields)) {
|
|
10
|
+
try {
|
|
11
|
+
out[name] = propToZod(prop.ast);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
throw new Error(`effectFieldsToZod: failed to convert field "${name}": ${err.message}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
};
|
|
18
|
+
var propToZod = (ast) => {
|
|
19
|
+
if (ast._tag === "PropertySignatureDeclaration") {
|
|
20
|
+
const description = readDescription(ast);
|
|
21
|
+
const isOptional = Boolean(ast.isOptional);
|
|
22
|
+
const innerAst = unwrapOptionalUnion(ast.type, isOptional);
|
|
23
|
+
let zod = astToZod(innerAst);
|
|
24
|
+
if (isOptional) {
|
|
25
|
+
zod = zod.optional();
|
|
26
|
+
}
|
|
27
|
+
if (description !== void 0) {
|
|
28
|
+
zod = zod.describe(description);
|
|
29
|
+
}
|
|
30
|
+
return zod;
|
|
31
|
+
}
|
|
32
|
+
return astToZod(ast);
|
|
33
|
+
};
|
|
34
|
+
var unwrapOptionalUnion = (ast, isOptional) => {
|
|
35
|
+
if (!isOptional || ast._tag !== "Union") {
|
|
36
|
+
return ast;
|
|
37
|
+
}
|
|
38
|
+
const types = ast.types.filter((t) => t._tag !== "UndefinedKeyword");
|
|
39
|
+
if (types.length === 1) {
|
|
40
|
+
return types[0];
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...ast,
|
|
44
|
+
types
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var astToZod = (ast) => {
|
|
48
|
+
let zod;
|
|
49
|
+
switch (ast._tag) {
|
|
50
|
+
case "StringKeyword":
|
|
51
|
+
zod = z.string();
|
|
52
|
+
break;
|
|
53
|
+
case "NumberKeyword":
|
|
54
|
+
zod = z.number();
|
|
55
|
+
break;
|
|
56
|
+
case "BooleanKeyword":
|
|
57
|
+
zod = z.boolean();
|
|
58
|
+
break;
|
|
59
|
+
case "Literal":
|
|
60
|
+
zod = z.literal(ast.literal);
|
|
61
|
+
break;
|
|
62
|
+
case "Union": {
|
|
63
|
+
const types = ast.types;
|
|
64
|
+
const allStringLiteral = types.every((t) => t._tag === "Literal" && typeof t.literal === "string");
|
|
65
|
+
if (!allStringLiteral) {
|
|
66
|
+
throw new Error(`unsupported Union \u2014 only enum-of-string-literals supported, got branches: ${types.map((t) => t._tag).join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
const values = types.map((t) => t.literal);
|
|
69
|
+
zod = z.enum(values);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "TupleType": {
|
|
73
|
+
const tuple = ast;
|
|
74
|
+
if (tuple.elements && tuple.elements.length > 0) {
|
|
75
|
+
throw new Error("fixed-length tuples are not supported \u2014 use Schema.Array(X)");
|
|
76
|
+
}
|
|
77
|
+
const elem = tuple.rest?.[0]?.type;
|
|
78
|
+
if (!elem) {
|
|
79
|
+
throw new Error("TupleType without rest element \u2014 Schema.Array(X) is the only supported array form");
|
|
80
|
+
}
|
|
81
|
+
zod = z.array(astToZod(elem));
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case "Refinement": {
|
|
85
|
+
const { base, jsonSchemas } = collectRefinements(ast);
|
|
86
|
+
let z0 = astToZod(base);
|
|
87
|
+
for (const js of jsonSchemas) {
|
|
88
|
+
z0 = applyJsonSchemaRefinement(z0, js);
|
|
89
|
+
}
|
|
90
|
+
zod = z0;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`unsupported Effect Schema AST node: ${ast._tag}`);
|
|
95
|
+
}
|
|
96
|
+
const description = readDescription(ast);
|
|
97
|
+
if (description !== void 0) {
|
|
98
|
+
zod = zod.describe(description);
|
|
99
|
+
}
|
|
100
|
+
return zod;
|
|
101
|
+
};
|
|
102
|
+
var collectRefinements = (ast) => {
|
|
103
|
+
const jsonSchemas = [];
|
|
104
|
+
let cursor = ast;
|
|
105
|
+
while (cursor._tag === "Refinement") {
|
|
106
|
+
const js = cursor.annotations?.[JSONSchemaAnnotationId];
|
|
107
|
+
if (typeof js === "object" && js !== null) {
|
|
108
|
+
jsonSchemas.push(js);
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error("Refinement is missing a JSONSchema annotation \u2014 only Effect stdlib refinements (int, positive, lessThanOrEqualTo, etc.) are currently supported");
|
|
111
|
+
}
|
|
112
|
+
cursor = cursor.from;
|
|
113
|
+
}
|
|
114
|
+
jsonSchemas.reverse();
|
|
115
|
+
return {
|
|
116
|
+
base: cursor,
|
|
117
|
+
jsonSchemas
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
var applyJsonSchemaRefinement = (zod, js) => {
|
|
121
|
+
let z0 = zod;
|
|
122
|
+
let touched = false;
|
|
123
|
+
if (js.type === "integer") {
|
|
124
|
+
z0 = z0.int();
|
|
125
|
+
touched = true;
|
|
126
|
+
}
|
|
127
|
+
if (typeof js.exclusiveMinimum === "number" && js.exclusiveMinimum === 0) {
|
|
128
|
+
z0 = z0.positive();
|
|
129
|
+
touched = true;
|
|
130
|
+
}
|
|
131
|
+
if (typeof js.maximum === "number") {
|
|
132
|
+
z0 = z0.max(js.maximum);
|
|
133
|
+
touched = true;
|
|
134
|
+
}
|
|
135
|
+
if (typeof js.minimum === "number") {
|
|
136
|
+
z0 = z0.min(js.minimum);
|
|
137
|
+
touched = true;
|
|
138
|
+
}
|
|
139
|
+
if (!touched) {
|
|
140
|
+
throw new Error(`unsupported JSONSchema refinement fragment: ${JSON.stringify(js)}`);
|
|
141
|
+
}
|
|
142
|
+
return z0;
|
|
143
|
+
};
|
|
144
|
+
var readDescription = (ast) => {
|
|
145
|
+
const annotations = ast.annotations;
|
|
146
|
+
if (!annotations) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
const value = annotations[DescriptionAnnotationId];
|
|
150
|
+
return typeof value === "string" ? value : void 0;
|
|
151
|
+
};
|
|
152
|
+
export {
|
|
153
|
+
effectFieldsToZod
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/effect-to-zod.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\n// Effect Schema → Zod converter, scoped to the patterns we use in MCP tool\n// inputs. The MCP SDK requires zod schemas for `inputSchema`, but we want to\n// author tool inputs in Effect Schema so:\n//\n// 1. The same definitions can be consumed by `react-ui-form` (which renders\n// forms from Effect Schema directly).\n// 2. We get Effect's annotation system (Description, Title, JSONSchema)\n// everywhere, plus refinements that compose cleanly with the rest of\n// the codebase.\n//\n// What's supported (anything outside this list throws at startup with a clear\n// message — strict denylist beats silent miscompilation):\n//\n// Schema.String → z.string()\n// Schema.Number → z.number()\n// Schema.Boolean → z.boolean()\n// Schema.Literal('a','b') → z.enum(['a','b'])\n// Schema.Array(x) → z.array(zodOf(x))\n// Schema.optional(x) → .optional()\n// Schema.int() → .int() (via JSONSchema annotation)\n// Schema.positive() → .positive() (via JSONSchema annotation)\n// Schema.lessThanOrEqualTo(n) → .max(n) (via JSONSchema annotation)\n// description annotation → .describe(...)\n//\n// We read refinements off the JSONSchema annotation Effect attaches to its\n// stdlib refinements, NOT off SchemaId symbols — that gives us a stable\n// integration point that doesn't break across Effect minor versions.\n\nimport * as Schema from 'effect/Schema';\nimport { z } from 'zod';\n\nconst DescriptionAnnotationId = Symbol.for('effect/annotation/Description');\nconst JSONSchemaAnnotationId = Symbol.for('effect/annotation/JSONSchema');\n\n/**\n * Convert the fields of an Effect `Schema.Struct(...)` into the\n * `Record<string, z.ZodTypeAny>` shape the MCP SDK's `registerTool` expects\n * for `inputSchema`.\n */\nexport const effectFieldsToZod = <Fields extends Schema.Struct.Fields>(\n schema: Schema.Struct<Fields>,\n): Record<keyof Fields & string, z.ZodTypeAny> => {\n const out: Record<string, z.ZodTypeAny> = {};\n for (const [name, prop] of Object.entries(schema.fields)) {\n try {\n out[name] = propToZod((prop as { ast: AnyAst }).ast);\n } catch (err) {\n throw new Error(`effectFieldsToZod: failed to convert field \"${name}\": ${(err as Error).message}`);\n }\n }\n return out as Record<keyof Fields & string, z.ZodTypeAny>;\n};\n\n/**\n * `Schema.PropertySignature` (the AST node a struct's `.fields[name].ast` is)\n * isn't part of the main `SchemaAST.AST` union — it's a separate hierarchy.\n * We use a structural type here that matches both shapes we encounter:\n *\n * - struct field signatures: `{ _tag: 'PropertySignatureDeclaration', type, isOptional, ... }`\n * - bare types (required fields, walked recursively): everything in `SchemaAST.AST`\n *\n * Treating `_tag` as a free string and downcasting selectively lets us handle\n * both without reaching for `as unknown as never` workarounds.\n */\ntype AnyAst = { _tag: string; annotations?: Record<symbol, unknown> } & Record<string, unknown>;\n\n/**\n * Convert one struct field's AST. Property signatures wrap the actual schema\n * AST with optional/readonly metadata; required fields are the AST directly.\n */\nconst propToZod = (ast: AnyAst): z.ZodTypeAny => {\n if (ast._tag === 'PropertySignatureDeclaration') {\n // `Schema.optional(X)` produces a PropertySignatureDeclaration whose `type`\n // is `Union(X, UndefinedKeyword)`. Peel UndefinedKeyword before recursing\n // so the converter operates on the user-facing type, then mark optional.\n const description = readDescription(ast);\n const isOptional = Boolean(ast.isOptional);\n const innerAst = unwrapOptionalUnion(ast.type as AnyAst, isOptional);\n let zod = astToZod(innerAst);\n if (isOptional) {\n zod = zod.optional();\n }\n if (description !== undefined) {\n zod = zod.describe(description);\n }\n return zod;\n }\n return astToZod(ast);\n};\n\n/**\n * Peel `UndefinedKeyword` from an optional field's union. Effect models\n * `optional(X)` as `Union(X, UndefinedKeyword)`; if that's what we have AND\n * the prop is optional, return X. Otherwise pass through unchanged.\n */\nconst unwrapOptionalUnion = (ast: AnyAst, isOptional: boolean): AnyAst => {\n if (!isOptional || ast._tag !== 'Union') {\n return ast;\n }\n const types = (ast.types as AnyAst[]).filter((t) => t._tag !== 'UndefinedKeyword');\n if (types.length === 1) {\n return types[0];\n }\n // Multi-branch union after stripping undefined — preserve as a Union; the\n // top-level switch handles literal-only unions (enums). Anything else\n // throws with a clear message.\n return { ...ast, types } as AnyAst;\n};\n\nconst astToZod = (ast: AnyAst): z.ZodTypeAny => {\n let zod: z.ZodTypeAny;\n switch (ast._tag) {\n case 'StringKeyword':\n zod = z.string();\n break;\n case 'NumberKeyword':\n zod = z.number();\n break;\n case 'BooleanKeyword':\n zod = z.boolean();\n break;\n case 'Literal':\n // `z.literal` accepts string | number | boolean | null. Effect's literal\n // value is already constrained to those by Schema.Literal's signature.\n zod = z.literal(ast.literal as string | number | boolean | null);\n break;\n case 'Union': {\n // Only support unions where every branch is a string literal — that's\n // what `Schema.Literal('a', 'b', 'c')` produces, and it maps directly\n // to `z.enum`. Other unions (mixed types, refinements) aren't currently\n // used in our tool inputs and would need a richer conversion.\n const types = ast.types as AnyAst[];\n const allStringLiteral = types.every((t) => t._tag === 'Literal' && typeof t.literal === 'string');\n if (!allStringLiteral) {\n throw new Error(\n `unsupported Union — only enum-of-string-literals supported, got branches: ${types.map((t) => t._tag).join(', ')}`,\n );\n }\n const values = types.map((t) => t.literal as string) as [string, ...string[]];\n zod = z.enum(values);\n break;\n }\n case 'TupleType': {\n // `Schema.Array(X)` produces a TupleType with a single rest element of\n // type X. Fixed tuples (`Schema.Tuple(...)`) aren't currently used.\n const tuple = ast as { rest?: ReadonlyArray<{ type: AnyAst }>; elements?: ReadonlyArray<unknown> };\n if (tuple.elements && tuple.elements.length > 0) {\n throw new Error('fixed-length tuples are not supported — use Schema.Array(X)');\n }\n const elem = tuple.rest?.[0]?.type;\n if (!elem) {\n throw new Error('TupleType without rest element — Schema.Array(X) is the only supported array form');\n }\n zod = z.array(astToZod(elem));\n break;\n }\n case 'Refinement': {\n // Walk the refinement chain down to the base type, collecting JSONSchema\n // annotations along the way. Apply each refinement's Zod equivalent on\n // top of the base. Order is deterministic: int → positive → max, so we\n // never trigger Zod's \"method must come after .int()\" sequencing rule.\n const { base, jsonSchemas } = collectRefinements(ast);\n let z0 = astToZod(base);\n for (const js of jsonSchemas) {\n z0 = applyJsonSchemaRefinement(z0, js);\n }\n zod = z0;\n break;\n }\n default:\n throw new Error(`unsupported Effect Schema AST node: ${ast._tag}`);\n }\n\n // Pass through Description annotation. This includes Effect's stdlib\n // defaults (\"a string\", \"a positive number\") if the user didn't override —\n // tool authors should always supply their own description for LLM trigger\n // accuracy, but we don't enforce that here.\n const description = readDescription(ast);\n if (description !== undefined) {\n zod = zod.describe(description);\n }\n return zod;\n};\n\nconst collectRefinements = (ast: AnyAst): { base: AnyAst; jsonSchemas: Array<Record<string, unknown>> } => {\n const jsonSchemas: Array<Record<string, unknown>> = [];\n let cursor: AnyAst = ast;\n while (cursor._tag === 'Refinement') {\n const js = cursor.annotations?.[JSONSchemaAnnotationId];\n if (typeof js === 'object' && js !== null) {\n jsonSchemas.push(js as Record<string, unknown>);\n } else {\n throw new Error(\n 'Refinement is missing a JSONSchema annotation — only Effect stdlib refinements (int, positive, lessThanOrEqualTo, etc.) are currently supported',\n );\n }\n cursor = cursor.from as AnyAst;\n }\n // Innermost refinements were pushed first; reverse so outer refinements\n // (e.g. `lessThanOrEqualTo`) apply LAST, after `.int().positive()` etc.\n jsonSchemas.reverse();\n return { base: cursor, jsonSchemas };\n};\n\n/**\n * Map a JSON Schema fragment from one Effect refinement to a Zod method on a\n * `z.number()`. We only translate the shapes Effect emits for its stdlib\n * refinements — anything else throws so the converter doesn't silently\n * generate an under-constrained Zod schema.\n */\nconst applyJsonSchemaRefinement = (zod: z.ZodTypeAny, js: Record<string, unknown>): z.ZodTypeAny => {\n // Only z.number() supports the methods we need. Defensive cast.\n let z0 = zod as z.ZodNumber;\n let touched = false;\n if (js.type === 'integer') {\n z0 = z0.int();\n touched = true;\n }\n if (typeof js.exclusiveMinimum === 'number' && js.exclusiveMinimum === 0) {\n z0 = z0.positive();\n touched = true;\n }\n if (typeof js.maximum === 'number') {\n z0 = z0.max(js.maximum);\n touched = true;\n }\n if (typeof js.minimum === 'number') {\n z0 = z0.min(js.minimum);\n touched = true;\n }\n if (!touched) {\n throw new Error(`unsupported JSONSchema refinement fragment: ${JSON.stringify(js)}`);\n }\n return z0;\n};\n\nconst readDescription = (ast: AnyAst): string | undefined => {\n const annotations = ast.annotations;\n if (!annotations) {\n return undefined;\n }\n const value = annotations[DescriptionAnnotationId];\n return typeof value === 'string' ? value : undefined;\n};\n"],
|
|
5
|
+
"mappings": ";;;AAiCA,SAASA,SAAS;AAElB,IAAMC,0BAA0BC,uBAAOC,IAAI,+BAAA;AAC3C,IAAMC,yBAAyBF,uBAAOC,IAAI,8BAAA;AAOnC,IAAME,oBAAoB,CAC/BC,WAAAA;AAEA,QAAMC,MAAoC,CAAC;AAC3C,aAAW,CAACC,MAAMC,IAAAA,KAASC,OAAOC,QAAQL,OAAOM,MAAM,GAAG;AACxD,QAAI;AACFL,UAAIC,IAAAA,IAAQK,UAAWJ,KAAyBK,GAAG;IACrD,SAASC,KAAK;AACZ,YAAM,IAAIC,MAAM,+CAA+CR,IAAAA,MAAWO,IAAcE,OAAO,EAAE;IACnG;EACF;AACA,SAAOV;AACT;AAmBA,IAAMM,YAAY,CAACC,QAAAA;AACjB,MAAIA,IAAII,SAAS,gCAAgC;AAI/C,UAAMC,cAAcC,gBAAgBN,GAAAA;AACpC,UAAMO,aAAaC,QAAQR,IAAIO,UAAU;AACzC,UAAME,WAAWC,oBAAoBV,IAAIW,MAAgBJ,UAAAA;AACzD,QAAIK,MAAMC,SAASJ,QAAAA;AACnB,QAAIF,YAAY;AACdK,YAAMA,IAAIE,SAAQ;IACpB;AACA,QAAIT,gBAAgBU,QAAW;AAC7BH,YAAMA,IAAII,SAASX,WAAAA;IACrB;AACA,WAAOO;EACT;AACA,SAAOC,SAASb,GAAAA;AAClB;AAOA,IAAMU,sBAAsB,CAACV,KAAaO,eAAAA;AACxC,MAAI,CAACA,cAAcP,IAAII,SAAS,SAAS;AACvC,WAAOJ;EACT;AACA,QAAMiB,QAASjB,IAAIiB,MAAmBC,OAAO,CAACC,MAAMA,EAAEf,SAAS,kBAAA;AAC/D,MAAIa,MAAMG,WAAW,GAAG;AACtB,WAAOH,MAAM,CAAA;EACf;AAIA,SAAO;IAAE,GAAGjB;IAAKiB;EAAM;AACzB;AAEA,IAAMJ,WAAW,CAACb,QAAAA;AAChB,MAAIY;AACJ,UAAQZ,IAAII,MAAI;IACd,KAAK;AACHQ,YAAM1B,EAAEmC,OAAM;AACd;IACF,KAAK;AACHT,YAAM1B,EAAEoC,OAAM;AACd;IACF,KAAK;AACHV,YAAM1B,EAAEqC,QAAO;AACf;IACF,KAAK;AAGHX,YAAM1B,EAAEsC,QAAQxB,IAAIwB,OAAO;AAC3B;IACF,KAAK,SAAS;AAKZ,YAAMP,QAAQjB,IAAIiB;AAClB,YAAMQ,mBAAmBR,MAAMS,MAAM,CAACP,MAAMA,EAAEf,SAAS,aAAa,OAAOe,EAAEK,YAAY,QAAA;AACzF,UAAI,CAACC,kBAAkB;AACrB,cAAM,IAAIvB,MACR,kFAA6Ee,MAAMU,IAAI,CAACR,MAAMA,EAAEf,IAAI,EAAEwB,KAAK,IAAA,CAAA,EAAO;MAEtH;AACA,YAAMC,SAASZ,MAAMU,IAAI,CAACR,MAAMA,EAAEK,OAAO;AACzCZ,YAAM1B,EAAE4C,KAAKD,MAAAA;AACb;IACF;IACA,KAAK,aAAa;AAGhB,YAAME,QAAQ/B;AACd,UAAI+B,MAAMC,YAAYD,MAAMC,SAASZ,SAAS,GAAG;AAC/C,cAAM,IAAIlB,MAAM,kEAAA;MAClB;AACA,YAAM+B,OAAOF,MAAMG,OAAO,CAAA,GAAIvB;AAC9B,UAAI,CAACsB,MAAM;AACT,cAAM,IAAI/B,MAAM,wFAAA;MAClB;AACAU,YAAM1B,EAAEiD,MAAMtB,SAASoB,IAAAA,CAAAA;AACvB;IACF;IACA,KAAK,cAAc;AAKjB,YAAM,EAAEG,MAAMC,YAAW,IAAKC,mBAAmBtC,GAAAA;AACjD,UAAIuC,KAAK1B,SAASuB,IAAAA;AAClB,iBAAWI,MAAMH,aAAa;AAC5BE,aAAKE,0BAA0BF,IAAIC,EAAAA;MACrC;AACA5B,YAAM2B;AACN;IACF;IACA;AACE,YAAM,IAAIrC,MAAM,uCAAuCF,IAAII,IAAI,EAAE;EACrE;AAMA,QAAMC,cAAcC,gBAAgBN,GAAAA;AACpC,MAAIK,gBAAgBU,QAAW;AAC7BH,UAAMA,IAAII,SAASX,WAAAA;EACrB;AACA,SAAOO;AACT;AAEA,IAAM0B,qBAAqB,CAACtC,QAAAA;AAC1B,QAAMqC,cAA8C,CAAA;AACpD,MAAIK,SAAiB1C;AACrB,SAAO0C,OAAOtC,SAAS,cAAc;AACnC,UAAMoC,KAAKE,OAAOC,cAAcrD,sBAAAA;AAChC,QAAI,OAAOkD,OAAO,YAAYA,OAAO,MAAM;AACzCH,kBAAYO,KAAKJ,EAAAA;IACnB,OAAO;AACL,YAAM,IAAItC,MACR,sJAAA;IAEJ;AACAwC,aAASA,OAAOG;EAClB;AAGAR,cAAYS,QAAO;AACnB,SAAO;IAAEV,MAAMM;IAAQL;EAAY;AACrC;AAQA,IAAMI,4BAA4B,CAAC7B,KAAmB4B,OAAAA;AAEpD,MAAID,KAAK3B;AACT,MAAImC,UAAU;AACd,MAAIP,GAAG7B,SAAS,WAAW;AACzB4B,SAAKA,GAAGS,IAAG;AACXD,cAAU;EACZ;AACA,MAAI,OAAOP,GAAGS,qBAAqB,YAAYT,GAAGS,qBAAqB,GAAG;AACxEV,SAAKA,GAAGW,SAAQ;AAChBH,cAAU;EACZ;AACA,MAAI,OAAOP,GAAGW,YAAY,UAAU;AAClCZ,SAAKA,GAAGa,IAAIZ,GAAGW,OAAO;AACtBJ,cAAU;EACZ;AACA,MAAI,OAAOP,GAAGa,YAAY,UAAU;AAClCd,SAAKA,GAAGe,IAAId,GAAGa,OAAO;AACtBN,cAAU;EACZ;AACA,MAAI,CAACA,SAAS;AACZ,UAAM,IAAI7C,MAAM,+CAA+CqD,KAAKC,UAAUhB,EAAAA,CAAAA,EAAK;EACrF;AACA,SAAOD;AACT;AAEA,IAAMjC,kBAAkB,CAACN,QAAAA;AACvB,QAAM2C,cAAc3C,IAAI2C;AACxB,MAAI,CAACA,aAAa;AAChB,WAAO5B;EACT;AACA,QAAM0C,QAAQd,YAAYxD,uBAAAA;AAC1B,SAAO,OAAOsE,UAAU,WAAWA,QAAQ1C;AAC7C;",
|
|
6
|
+
"names": ["z", "DescriptionAnnotationId", "Symbol", "for", "JSONSchemaAnnotationId", "effectFieldsToZod", "schema", "out", "name", "prop", "Object", "entries", "fields", "propToZod", "ast", "err", "Error", "message", "_tag", "description", "readDescription", "isOptional", "Boolean", "innerAst", "unwrapOptionalUnion", "type", "zod", "astToZod", "optional", "undefined", "describe", "types", "filter", "t", "length", "string", "number", "boolean", "literal", "allStringLiteral", "every", "map", "join", "values", "enum", "tuple", "elements", "elem", "rest", "array", "base", "jsonSchemas", "collectRefinements", "z0", "js", "applyJsonSchemaRefinement", "cursor", "annotations", "push", "from", "reverse", "touched", "int", "exclusiveMinimum", "positive", "maximum", "max", "minimum", "min", "JSON", "stringify", "value"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"src/effect-to-zod.ts":{"bytes":29727,"imports":[{"path":"zod","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":481,"imports":[{"path":"src/effect-to-zod.ts","kind":"import-statement","original":"./effect-to-zod"}],"format":"esm"}},"outputs":{"dist/lib/node-esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":14435},"dist/lib/node-esm/index.mjs":{"imports":[{"path":"zod","kind":"import-statement","external":true}],"exports":["effectFieldsToZod"],"entryPoint":"src/index.ts","inputs":{"src/effect-to-zod.ts":{"bytesInOutput":4314},"src/index.ts":{"bytesInOutput":0}},"bytes":4498}}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as Schema from 'effect/Schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Convert the fields of an Effect `Schema.Struct(...)` into the
|
|
5
|
+
* `Record<string, z.ZodTypeAny>` shape the MCP SDK's `registerTool` expects
|
|
6
|
+
* for `inputSchema`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const effectFieldsToZod: <Fields extends Schema.Struct.Fields>(schema: Schema.Struct<Fields>) => Record<keyof Fields & string, z.ZodTypeAny>;
|
|
9
|
+
//# sourceMappingURL=effect-to-zod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect-to-zod.d.ts","sourceRoot":"","sources":["../../../src/effect-to-zod.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,UAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAC5B,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,UAAU,CAU5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect-to-zod.test.d.ts","sourceRoot":"","sources":["../../../src/effect-to-zod.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|