@tsed/cli-mcp 7.0.0-rc.1 → 7.0.0-rc.3

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.
Files changed (71) hide show
  1. package/lib/esm/constants/constants.js +5 -0
  2. package/lib/esm/decorators/prompt.js +12 -0
  3. package/lib/esm/decorators/resource.js +15 -0
  4. package/lib/esm/decorators/tool.js +12 -0
  5. package/lib/esm/fn/definePrompt.js +31 -49
  6. package/lib/esm/fn/defineResource.js +28 -31
  7. package/lib/esm/fn/defineTool.js +58 -76
  8. package/lib/esm/services/McpServerFactory.js +24 -12
  9. package/lib/esm/utils/json-schema-to-zod/Types.js +1 -0
  10. package/lib/esm/utils/json-schema-to-zod/cli.js +74 -0
  11. package/lib/esm/utils/json-schema-to-zod/index.js +25 -0
  12. package/lib/esm/utils/json-schema-to-zod/jsonSchemaToZod.js +43 -0
  13. package/lib/esm/utils/json-schema-to-zod/parsers/parseAllOf.js +37 -0
  14. package/lib/esm/utils/json-schema-to-zod/parsers/parseAnyOf.js +11 -0
  15. package/lib/esm/utils/json-schema-to-zod/parsers/parseArray.js +16 -0
  16. package/lib/esm/utils/json-schema-to-zod/parsers/parseBoolean.js +3 -0
  17. package/lib/esm/utils/json-schema-to-zod/parsers/parseConst.js +3 -0
  18. package/lib/esm/utils/json-schema-to-zod/parsers/parseDefault.js +3 -0
  19. package/lib/esm/utils/json-schema-to-zod/parsers/parseEnum.js +15 -0
  20. package/lib/esm/utils/json-schema-to-zod/parsers/parseIfThenElse.js +20 -0
  21. package/lib/esm/utils/json-schema-to-zod/parsers/parseMultipleType.js +4 -0
  22. package/lib/esm/utils/json-schema-to-zod/parsers/parseNot.js +7 -0
  23. package/lib/esm/utils/json-schema-to-zod/parsers/parseNull.js +3 -0
  24. package/lib/esm/utils/json-schema-to-zod/parsers/parseNullable.js +8 -0
  25. package/lib/esm/utils/json-schema-to-zod/parsers/parseNumber.js +46 -0
  26. package/lib/esm/utils/json-schema-to-zod/parsers/parseObject.js +171 -0
  27. package/lib/esm/utils/json-schema-to-zod/parsers/parseOneOf.js +34 -0
  28. package/lib/esm/utils/json-schema-to-zod/parsers/parseSchema.js +138 -0
  29. package/lib/esm/utils/json-schema-to-zod/parsers/parseString.js +57 -0
  30. package/lib/esm/utils/json-schema-to-zod/utils/cliTools.js +99 -0
  31. package/lib/esm/utils/json-schema-to-zod/utils/half.js +3 -0
  32. package/lib/esm/utils/json-schema-to-zod/utils/jsdocs.js +12 -0
  33. package/lib/esm/utils/json-schema-to-zod/utils/omit.js +6 -0
  34. package/lib/esm/utils/json-schema-to-zod/utils/withMessage.js +19 -0
  35. package/lib/esm/utils/toZod.js +2 -2
  36. package/lib/tsconfig.esm.tsbuildinfo +1 -1
  37. package/lib/types/constants/constants.d.ts +5 -0
  38. package/lib/types/decorators/prompt.d.ts +3 -0
  39. package/lib/types/decorators/resource.d.ts +4 -0
  40. package/lib/types/decorators/tool.d.ts +2 -0
  41. package/lib/types/fn/definePrompt.d.ts +42 -38
  42. package/lib/types/fn/defineResource.d.ts +2 -29
  43. package/lib/types/fn/defineTool.d.ts +128 -40
  44. package/lib/types/utils/json-schema-to-zod/Types.d.ts +68 -0
  45. package/lib/types/utils/json-schema-to-zod/cli.d.ts +2 -0
  46. package/lib/types/utils/json-schema-to-zod/index.d.ts +25 -0
  47. package/lib/types/utils/json-schema-to-zod/jsonSchemaToZod.d.ts +2 -0
  48. package/lib/types/utils/json-schema-to-zod/parsers/parseAllOf.d.ts +4 -0
  49. package/lib/types/utils/json-schema-to-zod/parsers/parseAnyOf.d.ts +4 -0
  50. package/lib/types/utils/json-schema-to-zod/parsers/parseArray.d.ts +4 -0
  51. package/lib/types/utils/json-schema-to-zod/parsers/parseBoolean.d.ts +4 -0
  52. package/lib/types/utils/json-schema-to-zod/parsers/parseConst.d.ts +4 -0
  53. package/lib/types/utils/json-schema-to-zod/parsers/parseDefault.d.ts +2 -0
  54. package/lib/types/utils/json-schema-to-zod/parsers/parseEnum.d.ts +4 -0
  55. package/lib/types/utils/json-schema-to-zod/parsers/parseIfThenElse.d.ts +6 -0
  56. package/lib/types/utils/json-schema-to-zod/parsers/parseMultipleType.d.ts +4 -0
  57. package/lib/types/utils/json-schema-to-zod/parsers/parseNot.d.ts +4 -0
  58. package/lib/types/utils/json-schema-to-zod/parsers/parseNull.d.ts +4 -0
  59. package/lib/types/utils/json-schema-to-zod/parsers/parseNullable.d.ts +7 -0
  60. package/lib/types/utils/json-schema-to-zod/parsers/parseNumber.d.ts +4 -0
  61. package/lib/types/utils/json-schema-to-zod/parsers/parseObject.d.ts +4 -0
  62. package/lib/types/utils/json-schema-to-zod/parsers/parseOneOf.d.ts +4 -0
  63. package/lib/types/utils/json-schema-to-zod/parsers/parseSchema.d.ts +46 -0
  64. package/lib/types/utils/json-schema-to-zod/parsers/parseString.d.ts +4 -0
  65. package/lib/types/utils/json-schema-to-zod/utils/cliTools.d.ts +28 -0
  66. package/lib/types/utils/json-schema-to-zod/utils/half.d.ts +1 -0
  67. package/lib/types/utils/json-schema-to-zod/utils/jsdocs.d.ts +3 -0
  68. package/lib/types/utils/json-schema-to-zod/utils/omit.d.ts +1 -0
  69. package/lib/types/utils/json-schema-to-zod/utils/withMessage.d.ts +10 -0
  70. package/lib/types/utils/toZod.d.ts +2 -2
  71. package/package.json +5 -5
@@ -0,0 +1,5 @@
1
+ export const MCP_PROVIDER_TYPES = {
2
+ TOOL: "mcp:tool",
3
+ RESOURCE: "mcp:resource",
4
+ PROMPT: "mcp:prompt"
5
+ };
@@ -0,0 +1,12 @@
1
+ import { classOf } from "@tsed/core";
2
+ import { definePrompt } from "../fn/definePrompt.js";
3
+ export function Prompt(options) {
4
+ return (target, propertyKey, _) => {
5
+ definePrompt({
6
+ ...options,
7
+ name: options?.name || String(propertyKey),
8
+ token: classOf(target),
9
+ propertyKey: propertyKey
10
+ });
11
+ };
12
+ }
@@ -0,0 +1,15 @@
1
+ import { classOf } from "@tsed/core";
2
+ import { isString } from "@tsed/core/utils/isString.js";
3
+ import { defineResource } from "../fn/defineResource.js";
4
+ export function Resource(uriOrTemplate, options) {
5
+ return (target, propertyKey, _) => {
6
+ defineResource({
7
+ ...options,
8
+ name: options?.name || String(propertyKey),
9
+ token: classOf(target),
10
+ propertyKey,
11
+ uri: isString(uriOrTemplate) ? uriOrTemplate : undefined,
12
+ template: !isString(uriOrTemplate) ? uriOrTemplate : undefined
13
+ });
14
+ };
15
+ }
@@ -0,0 +1,12 @@
1
+ import { classOf } from "@tsed/core";
2
+ import { defineTool } from "../fn/defineTool.js";
3
+ export function Tool(name, options = {}) {
4
+ return (target, propertyKey, _) => {
5
+ defineTool({
6
+ ...options,
7
+ name: name || String(propertyKey),
8
+ token: classOf(target),
9
+ propertyKey
10
+ });
11
+ };
12
+ }
@@ -1,52 +1,34 @@
1
- import { injectable } from "@tsed/cli-core";
2
- import { DIContext, injector, logger, runInContext } from "@tsed/di";
3
- import { v4 } from "uuid";
4
- /**
5
- * Prompts are reusable templates that help humans prompt models to interact with your server.
6
- * They're designed to be user-driven, and might appear as slash commands in a chat interface.
7
- *
8
- * ```ts
9
- * import {definePrompt} from "@tsed/cli-mcp";
10
- *
11
- * export default definePrompt({
12
- * name: "review-code",
13
- * title: 'Code review',
14
- * description: 'Review code for best practices and potential issues',
15
- * argsSchema: { code: z.string() }
16
- * handler: ({ code }) => ({
17
- * messages: [
18
- * {
19
- * role: 'user',
20
- * content: {
21
- * type: 'text',
22
- * text: `Please review this code:\n\n${code}`
23
- * }
24
- * }
25
- * ]
26
- * })
27
- * });
28
- * ```
29
- *
30
- * @param options {PromptProps}
31
- */
32
- export function definePrompt(options) {
33
- const provider = injectable(options.token || Symbol.for(`MCP:RESOURCE:${options.name}`))
34
- .type("CLI_MCP_RESOURCES")
35
- .factory(() => ({
1
+ import { isArrowFn } from "@tsed/core";
2
+ import { inject, injectable } from "@tsed/di";
3
+ import { JsonEntityStore, JsonSchema } from "@tsed/schema";
4
+ import { MCP_PROVIDER_TYPES } from "../constants/constants.js";
5
+ import { toZod } from "../utils/toZod.js";
6
+ function mapOptions(options) {
7
+ let handler = undefined;
8
+ if ("handler" in options) {
9
+ handler = options.handler;
10
+ }
11
+ if ("propertyKey" in options && options.propertyKey) {
12
+ const { token, propertyKey } = options;
13
+ handler = ((...args) => {
14
+ const instance = inject(options.token);
15
+ return instance[propertyKey](...args);
16
+ });
17
+ const methodStore = JsonEntityStore.fromMethod(token, propertyKey);
18
+ options.description = options.description || methodStore.operation.get("description");
19
+ options.title = options.title || methodStore.schema.get("title");
20
+ }
21
+ return {
36
22
  ...options,
37
- async handler(...args) {
38
- const $ctx = new DIContext({
39
- id: v4(),
40
- injector: injector(),
41
- logger: logger(),
42
- level: logger().level,
43
- maxStackSize: 0,
44
- platform: "MCP"
45
- });
46
- return runInContext($ctx, () => {
47
- return options.handler(...args);
48
- });
49
- }
50
- }));
23
+ argsSchema: toZod(isArrowFn(options.argsSchema) ? options.argsSchema() : options.argsSchema),
24
+ handler: handler
25
+ };
26
+ }
27
+ export function definePrompt(options) {
28
+ const provider = injectable(Symbol.for(`MCP:PROMPT:${options.name}`))
29
+ .type(MCP_PROVIDER_TYPES.PROMPT)
30
+ .factory(() => {
31
+ return mapOptions(options);
32
+ });
51
33
  return provider.token();
52
34
  }
@@ -1,34 +1,31 @@
1
- import { injectable } from "@tsed/cli-core";
2
- import { DIContext, injector, logger, runInContext } from "@tsed/di";
3
- import { v4 } from "uuid";
4
- export function defineResource(options) {
5
- const provider = injectable(options.token || Symbol.for(`MCP:RESOURCE:${options.name}`))
6
- .type("CLI_MCP_RESOURCES")
7
- .factory(() => ({
1
+ import { inject, injectable } from "@tsed/di";
2
+ import { JsonEntityStore } from "@tsed/schema";
3
+ import { MCP_PROVIDER_TYPES } from "../constants/constants.js";
4
+ function mapOptions(options) {
5
+ let handler;
6
+ if ("propertyKey" in options && options.propertyKey) {
7
+ const { token, propertyKey } = options;
8
+ handler = (...args) => {
9
+ const instance = inject(options.token);
10
+ return instance[propertyKey](...args);
11
+ };
12
+ const methodStore = JsonEntityStore.fromMethod(token, propertyKey);
13
+ options.description = options.description || methodStore.operation.get("description");
14
+ options.title = options.title || methodStore.schema.get("title");
15
+ }
16
+ else {
17
+ handler = options.handler;
18
+ }
19
+ return {
8
20
  ...options,
9
- async handler(...args) {
10
- const $ctx = new DIContext({
11
- id: v4(),
12
- injector: injector(),
13
- logger: logger(),
14
- level: logger().level,
15
- maxStackSize: 0,
16
- platform: "MCP"
17
- });
18
- try {
19
- return await runInContext($ctx, () => {
20
- return options.handler(...args);
21
- });
22
- }
23
- finally {
24
- try {
25
- await $ctx.destroy();
26
- }
27
- catch {
28
- // ignore
29
- }
30
- }
31
- }
32
- }));
21
+ handler
22
+ };
23
+ }
24
+ export function defineResource(options) {
25
+ const provider = injectable(Symbol.for(`MCP:RESOURCE:${options.name}`))
26
+ .type(MCP_PROVIDER_TYPES.RESOURCE)
27
+ .factory(() => {
28
+ return mapOptions(options);
29
+ });
33
30
  return provider.token();
34
31
  }
@@ -1,85 +1,67 @@
1
- import { injectable } from "@tsed/cli-core";
2
1
  import { isArrowFn } from "@tsed/core";
3
- import { DIContext, injector, logger, runInContext } from "@tsed/di";
4
- import { JsonSchema } from "@tsed/schema";
5
- import { v4 } from "uuid";
2
+ import { inject, injectable, logger } from "@tsed/di";
3
+ import { JsonEntityStore, JsonMethodStore, JsonSchema } from "@tsed/schema";
4
+ import { MCP_PROVIDER_TYPES } from "../constants/constants.js";
6
5
  import { toZod } from "../utils/toZod.js";
7
- /**
8
- * Tools let LLMs take actions through your server. Tools can perform computation, fetch data and have side effects.
9
- * Tools should be designed to be model-controlled - i.e. AI models will decide which tools to call, and the arguments.
10
- *
11
- * ```typescript
12
- * import {defineTool} from "@tsed/cli-mcp";
13
- * import {s} from "@tsed/schema";
14
- *
15
- * export default defineTool({
16
- * name: "my-tool",
17
- * title: "My Tool",
18
- * description: "My tool description",
19
- * inputSchema: s.object({
20
- * param1: s.string().required() // also support Zod
21
- * }),
22
- * outputSchema: s.object({
23
- * result: s.string().required() // also support Zod
24
- * }),
25
- * handler(args) {
26
- * return {
27
- * content: [],
28
- * structuredContent: {
29
- * result: "Hello World!"
30
- * }
31
- * }
32
- * });
33
- * ```
34
- *
35
- * @param options
36
- */
37
- export function defineTool(options) {
38
- const provider = injectable(options.token || Symbol.for(`MCP:TOOL:${options.name}`))
39
- .type("CLI_MCP_TOOLS")
40
- .factory(() => ({
6
+ function getOutputSchema(methodStore) {
7
+ const schema = methodStore.operation.getResponseOf(200)?.getMedia("application/json")?.get("schema");
8
+ return schema?.itemSchema();
9
+ }
10
+ function getInputSchema(token, propertyKey) {
11
+ return JsonEntityStore.from(token, propertyKey, 0).schema?.itemSchema();
12
+ }
13
+ function mapOptions(options) {
14
+ let handler;
15
+ if ("propertyKey" in options) {
16
+ const { token, propertyKey } = options;
17
+ handler = (args, extra) => {
18
+ const instance = inject(options.token);
19
+ return instance[options.propertyKey](args, extra);
20
+ };
21
+ const methodStore = JsonEntityStore.fromMethod(token, propertyKey);
22
+ options.description = options.description || methodStore.operation.get("description");
23
+ options.inputSchema = options.inputSchema || getInputSchema(token, propertyKey);
24
+ options.outputSchema = options.outputSchema || getOutputSchema(methodStore);
25
+ }
26
+ else {
27
+ handler = options.handler;
28
+ }
29
+ return {
41
30
  ...options,
42
- inputSchema: toZod(isArrowFn(options.inputSchema) ? options.inputSchema() : options.inputSchema),
43
- outputSchema: toZod(options.outputSchema),
44
- async handler(args, extra) {
45
- const $ctx = new DIContext({
46
- id: v4(),
47
- injector: injector(),
48
- logger: logger(),
49
- level: logger().level,
50
- maxStackSize: 0,
51
- platform: "MCP"
52
- });
53
- try {
54
- return await runInContext($ctx, () => {
55
- return options.handler(args, extra);
56
- });
57
- }
58
- catch (er) {
59
- $ctx.logger.error({
60
- event: "MCP_TOOL_ERROR",
61
- tool: options.name,
62
- error_message: er.message,
63
- stack: er.stack
64
- });
65
- return {
66
- content: [],
67
- structuredContent: {
68
- code: "E_MCP_TOOL_ERROR",
69
- message: er.message
70
- }
71
- };
72
- }
73
- finally {
74
- // Ensure per-invocation context is destroyed to avoid leaks
31
+ handler
32
+ };
33
+ }
34
+ export function defineTool(options) {
35
+ const provider = injectable(Symbol.for(`MCP:TOOL:${options.name}`))
36
+ .type(MCP_PROVIDER_TYPES.TOOL)
37
+ .factory(() => {
38
+ let { handler, ...opts } = mapOptions(options);
39
+ return {
40
+ ...opts,
41
+ name: opts.name,
42
+ inputSchema: toZod(isArrowFn(opts.inputSchema) ? opts.inputSchema() : opts.inputSchema),
43
+ outputSchema: toZod(opts.outputSchema),
44
+ async handler(args, extra) {
75
45
  try {
76
- await $ctx.destroy();
46
+ return await handler(args, extra);
77
47
  }
78
- catch {
79
- // ignore
48
+ catch (er) {
49
+ logger().error({
50
+ event: "MCP_TOOL_ERROR",
51
+ tool: opts.name,
52
+ error_message: er?.message,
53
+ stack: er?.stack
54
+ });
55
+ return {
56
+ content: [],
57
+ structuredContent: {
58
+ code: "E_MCP_TOOL_ERROR",
59
+ message: er?.message
60
+ }
61
+ };
80
62
  }
81
63
  }
82
- }
83
- }));
64
+ };
65
+ });
84
66
  return provider.token();
85
67
  }
@@ -1,28 +1,40 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { constant, inject, injectable, logger } from "@tsed/di";
2
+ import { constant, inject, injectable, injector, logger } from "@tsed/di";
3
+ import { MCP_PROVIDER_TYPES } from "../constants/constants.js";
3
4
  import { mcpStdioServer } from "./McpStdioServer.js";
4
5
  import { mcpStreamableServer } from "./McpStreamableServer.js";
6
+ function collectTokens(type, configured = []) {
7
+ const tokens = new Set(configured);
8
+ injector()
9
+ .getProviders(type)
10
+ .forEach((provider) => tokens.add(provider.token));
11
+ return [...tokens];
12
+ }
5
13
  export const MCP_SERVER = injectable(McpServer)
6
14
  .factory(() => {
7
15
  const defaultMode = constant("mcp.mode");
8
- const name = constant("name");
16
+ const name = constant("name", "tsed-mcp");
17
+ const version = constant("version", "0.0.0");
9
18
  const server = new McpServer({
10
19
  name,
11
- version: constant("pkg.version")
20
+ version
12
21
  });
13
- const tools = constant("tools", []);
14
- tools.map((token) => {
15
- const { name, handler, ...opts } = inject(token);
22
+ const toolTokens = collectTokens(MCP_PROVIDER_TYPES.TOOL, constant("tools", []));
23
+ toolTokens.forEach((token) => {
24
+ const definition = inject(token);
25
+ const { name, handler, ...opts } = definition;
16
26
  server.registerTool(name, opts, handler);
17
27
  });
18
- const resources = constant("resources", []);
19
- resources.map((token) => {
20
- const { name, handler, uri, template, ...opts } = inject(token);
28
+ const resourceTokens = collectTokens(MCP_PROVIDER_TYPES.RESOURCE, constant("resources", []));
29
+ resourceTokens.forEach((token) => {
30
+ const definition = inject(token);
31
+ const { name, handler, uri, template, ...opts } = definition;
21
32
  server.registerResource(name, (uri || template), opts, handler);
22
33
  });
23
- const prompts = constant("prompts", []);
24
- prompts.map((token) => {
25
- const { name, handler, ...opts } = inject(token);
34
+ const promptTokens = collectTokens(MCP_PROVIDER_TYPES.PROMPT, constant("prompts", []));
35
+ promptTokens.forEach((token) => {
36
+ const definition = inject(token);
37
+ const { name, handler, ...opts } = definition;
26
38
  server.registerPrompt(name, opts, handler);
27
39
  });
28
40
  return {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { mkdirSync, writeFileSync } from "fs";
3
+ import { dirname } from "path";
4
+ import { jsonSchemaToZod } from "./jsonSchemaToZod.js";
5
+ import { parseArgs, parseOrReadJSON, readPipe } from "./utils/cliTools.js";
6
+ const params = {
7
+ input: {
8
+ shorthand: "i",
9
+ value: "string",
10
+ required: process.stdin.isTTY && "input is required when no JSON or file path is piped",
11
+ description: "JSON or a source file path. Required if no data is piped."
12
+ },
13
+ output: {
14
+ shorthand: "o",
15
+ value: "string",
16
+ description: "A file path to write to. If not supplied stdout will be used."
17
+ },
18
+ name: {
19
+ shorthand: "n",
20
+ value: "string",
21
+ description: "The name of the schema in the output."
22
+ },
23
+ depth: {
24
+ shorthand: "d",
25
+ value: "number",
26
+ description: "Maximum depth of recursion before falling back to z.any(). Defaults to 0."
27
+ },
28
+ module: {
29
+ shorthand: "m",
30
+ value: ["esm", "cjs", "none"],
31
+ description: "Module syntax; 'esm', 'cjs' or 'none'. Defaults to 'esm'."
32
+ },
33
+ type: {
34
+ shorthand: "t",
35
+ value: "string",
36
+ description: "The name of the (optional) inferred type export."
37
+ },
38
+ noImport: {
39
+ shorthand: "ni",
40
+ description: "Removes the `import { z } from 'zod';` or equivalent from the output."
41
+ },
42
+ withJsdocs: {
43
+ shorthand: "wj",
44
+ description: "Generate jsdocs off of the description property."
45
+ },
46
+ zodVersion: {
47
+ shorthand: "zv",
48
+ value: "number",
49
+ description: "Target Zod version: 3 or 4. Defaults to 4."
50
+ }
51
+ };
52
+ async function main() {
53
+ const args = parseArgs(params, process.argv, true);
54
+ const input = args.input || (await readPipe());
55
+ const jsonSchema = parseOrReadJSON(input);
56
+ const zodVersion = (args.zodVersion === 3 ? 3 : 4);
57
+ const zodSchema = jsonSchemaToZod(jsonSchema, {
58
+ name: args.name,
59
+ depth: args.depth,
60
+ module: args.module || "esm",
61
+ noImport: args.noImport,
62
+ type: args.type,
63
+ withJsdocs: args.withJsdocs,
64
+ zodVersion
65
+ });
66
+ if (args.output) {
67
+ mkdirSync(dirname(args.output), { recursive: true });
68
+ writeFileSync(args.output, zodSchema);
69
+ }
70
+ else {
71
+ console.log(zodSchema);
72
+ }
73
+ }
74
+ void main();
@@ -0,0 +1,25 @@
1
+ export * from "./jsonSchemaToZod.js";
2
+ export * from "./parsers/parseAllOf.js";
3
+ export * from "./parsers/parseAnyOf.js";
4
+ export * from "./parsers/parseArray.js";
5
+ export * from "./parsers/parseBoolean.js";
6
+ export * from "./parsers/parseConst.js";
7
+ export * from "./parsers/parseDefault.js";
8
+ export * from "./parsers/parseEnum.js";
9
+ export * from "./parsers/parseIfThenElse.js";
10
+ export * from "./parsers/parseMultipleType.js";
11
+ export * from "./parsers/parseNot.js";
12
+ export * from "./parsers/parseNull.js";
13
+ export * from "./parsers/parseNullable.js";
14
+ export * from "./parsers/parseNumber.js";
15
+ export * from "./parsers/parseObject.js";
16
+ export * from "./parsers/parseOneOf.js";
17
+ export * from "./parsers/parseSchema.js";
18
+ export * from "./parsers/parseString.js";
19
+ export * from "./Types.js";
20
+ export * from "./utils/half.js";
21
+ export * from "./utils/jsdocs.js";
22
+ export * from "./utils/omit.js";
23
+ export * from "./utils/withMessage.js";
24
+ import { jsonSchemaToZod } from "./jsonSchemaToZod.js";
25
+ export default jsonSchemaToZod;
@@ -0,0 +1,43 @@
1
+ import { parseSchema } from "./parsers/parseSchema.js";
2
+ import { expandJsdocs } from "./utils/jsdocs.js";
3
+ export const jsonSchemaToZod = (schema, { module, name, type, noImport, zodVersion = 4, ...rest } = {}) => {
4
+ if (type && (!name || module !== "esm")) {
5
+ throw new Error("Option `type` requires `name` to be set and `module` to be `esm`");
6
+ }
7
+ let result = parseSchema(schema, {
8
+ module,
9
+ name,
10
+ path: [],
11
+ seen: new Map(),
12
+ zodVersion,
13
+ ...rest
14
+ });
15
+ const jsdocs = rest.withJsdocs && typeof schema !== "boolean" && schema.description ? expandJsdocs(schema.description) : "";
16
+ if (module === "cjs") {
17
+ result = `${jsdocs}module.exports = ${name ? `{ ${JSON.stringify(name)}: ${result} }` : result}
18
+ `;
19
+ if (!noImport) {
20
+ result = `${jsdocs}const { z } = require("zod")
21
+
22
+ ${result}`;
23
+ }
24
+ }
25
+ else if (module === "esm") {
26
+ result = `${jsdocs}export ${name ? `const ${name} =` : `default`} ${result}
27
+ `;
28
+ if (!noImport) {
29
+ result = `import { z } from "zod"
30
+
31
+ ${result}`;
32
+ }
33
+ }
34
+ else if (name) {
35
+ result = `${jsdocs}const ${name} = ${result}`;
36
+ }
37
+ if (type && name) {
38
+ let typeName = typeof type === "string" ? type : `${name[0].toUpperCase()}${name.substring(1)}`;
39
+ result += `export type ${typeName} = z.infer<typeof ${name}>
40
+ `;
41
+ }
42
+ return result;
43
+ };
@@ -0,0 +1,37 @@
1
+ import { half } from "../utils/half.js";
2
+ import { parseSchema } from "./parseSchema.js";
3
+ const originalIndex = Symbol("Original index");
4
+ const ensureOriginalIndex = (arr) => {
5
+ let newArr = [];
6
+ for (let i = 0; i < arr.length; i++) {
7
+ const item = arr[i];
8
+ if (typeof item === "boolean") {
9
+ newArr.push(item ? { [originalIndex]: i } : { [originalIndex]: i, not: {} });
10
+ }
11
+ else if (originalIndex in item) {
12
+ return arr;
13
+ }
14
+ else {
15
+ newArr.push({ ...item, [originalIndex]: i });
16
+ }
17
+ }
18
+ return newArr;
19
+ };
20
+ export function parseAllOf(schema, refs) {
21
+ if (schema.allOf.length === 0) {
22
+ return "z.never()";
23
+ }
24
+ else if (schema.allOf.length === 1) {
25
+ const item = schema.allOf[0];
26
+ return parseSchema(item, {
27
+ ...refs,
28
+ path: [...refs.path, "allOf", item[originalIndex]]
29
+ });
30
+ }
31
+ else {
32
+ const [left, right] = half(ensureOriginalIndex(schema.allOf));
33
+ return `z.intersection(${parseAllOf({ allOf: left }, refs)}, ${parseAllOf({
34
+ allOf: right
35
+ }, refs)})`;
36
+ }
37
+ }
@@ -0,0 +1,11 @@
1
+ import { parseSchema } from "./parseSchema.js";
2
+ export const parseAnyOf = (schema, refs) => {
3
+ return schema.anyOf.length
4
+ ? schema.anyOf.length === 1
5
+ ? parseSchema(schema.anyOf[0], {
6
+ ...refs,
7
+ path: [...refs.path, "anyOf", 0]
8
+ })
9
+ : `z.union([${schema.anyOf.map((schema, i) => parseSchema(schema, { ...refs, path: [...refs.path, "anyOf", i] })).join(", ")}])`
10
+ : `z.any()`;
11
+ };
@@ -0,0 +1,16 @@
1
+ import { withMessage } from "../utils/withMessage.js";
2
+ import { parseSchema } from "./parseSchema.js";
3
+ export const parseArray = (schema, refs) => {
4
+ if (Array.isArray(schema.items)) {
5
+ return `z.tuple([${schema.items.map((v, i) => parseSchema(v, { ...refs, path: [...refs.path, "items", i] }))}])`;
6
+ }
7
+ let r = !schema.items
8
+ ? "z.array(z.any())"
9
+ : `z.array(${parseSchema(schema.items, {
10
+ ...refs,
11
+ path: [...refs.path, "items"]
12
+ })})`;
13
+ r += withMessage(schema, "minItems", ({ json }) => [`.min(${json}`, ", ", ")"]);
14
+ r += withMessage(schema, "maxItems", ({ json }) => [`.max(${json}`, ", ", ")"]);
15
+ return r;
16
+ };
@@ -0,0 +1,3 @@
1
+ export const parseBoolean = (_schema) => {
2
+ return "z.boolean()";
3
+ };
@@ -0,0 +1,3 @@
1
+ export const parseConst = (schema) => {
2
+ return `z.literal(${JSON.stringify(schema.const)})`;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const parseDefault = (_schema) => {
2
+ return "z.any()";
3
+ };
@@ -0,0 +1,15 @@
1
+ export const parseEnum = (schema) => {
2
+ if (schema.enum.length === 0) {
3
+ return "z.never()";
4
+ }
5
+ else if (schema.enum.length === 1) {
6
+ // union does not work when there is only one element
7
+ return `z.literal(${JSON.stringify(schema.enum[0])})`;
8
+ }
9
+ else if (schema.enum.every((x) => typeof x === "string")) {
10
+ return `z.enum([${schema.enum.map((x) => JSON.stringify(x))}])`;
11
+ }
12
+ else {
13
+ return `z.union([${schema.enum.map((x) => `z.literal(${JSON.stringify(x)})`).join(", ")}])`;
14
+ }
15
+ };