@danielfgray/pg-sourcerer 0.3.0 → 0.4.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.
Files changed (307) hide show
  1. package/bin/pgsourcerer +2 -0
  2. package/dist/__tests__/fixtures/index.d.ts +15 -0
  3. package/dist/__tests__/fixtures/index.d.ts.map +1 -0
  4. package/dist/__tests__/fixtures/index.js +19 -0
  5. package/dist/__tests__/fixtures/index.js.map +1 -0
  6. package/dist/__tests__/fixtures/introspection.json +40522 -0
  7. package/dist/cli.d.ts +0 -1
  8. package/dist/cli.js +7 -46
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +38 -5
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +13 -2
  13. package/dist/config.js.map +1 -1
  14. package/dist/{lib/conjure.d.ts → conjure/index.d.ts} +62 -3
  15. package/dist/conjure/index.d.ts.map +1 -0
  16. package/dist/{lib/conjure.js → conjure/index.js} +124 -3
  17. package/dist/conjure/index.js.map +1 -0
  18. package/dist/conjure/signature.d.ts +85 -0
  19. package/dist/conjure/signature.d.ts.map +1 -0
  20. package/dist/conjure/signature.js +130 -0
  21. package/dist/conjure/signature.js.map +1 -0
  22. package/dist/conjure/types.d.ts +97 -0
  23. package/dist/conjure/types.d.ts.map +1 -0
  24. package/dist/conjure/types.js +206 -0
  25. package/dist/conjure/types.js.map +1 -0
  26. package/dist/errors.d.ts +114 -139
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/errors.js +82 -36
  29. package/dist/errors.js.map +1 -1
  30. package/dist/generate.d.ts +45 -46
  31. package/dist/generate.d.ts.map +1 -1
  32. package/dist/generate.js +86 -59
  33. package/dist/generate.js.map +1 -1
  34. package/dist/hex/builder.d.ts +12 -0
  35. package/dist/hex/builder.d.ts.map +1 -0
  36. package/dist/hex/builder.js +64 -0
  37. package/dist/hex/builder.js.map +1 -0
  38. package/dist/hex/ddl.d.ts +53 -0
  39. package/dist/hex/ddl.d.ts.map +1 -0
  40. package/dist/hex/ddl.js +306 -0
  41. package/dist/hex/ddl.js.map +1 -0
  42. package/dist/hex/index.d.ts +105 -0
  43. package/dist/hex/index.d.ts.map +1 -0
  44. package/dist/hex/index.js +81 -0
  45. package/dist/hex/index.js.map +1 -0
  46. package/dist/hex/primitives.d.ts +23 -0
  47. package/dist/hex/primitives.d.ts.map +1 -0
  48. package/dist/hex/primitives.js +38 -0
  49. package/dist/hex/primitives.js.map +1 -0
  50. package/dist/hex/query.d.ts +116 -0
  51. package/dist/hex/query.d.ts.map +1 -0
  52. package/dist/hex/query.js +219 -0
  53. package/dist/hex/query.js.map +1 -0
  54. package/dist/hex/types.d.ts +287 -0
  55. package/dist/hex/types.d.ts.map +1 -0
  56. package/dist/hex/types.js +431 -0
  57. package/dist/hex/types.js.map +1 -0
  58. package/dist/index.d.ts +17 -25
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +33 -44
  61. package/dist/index.js.map +1 -1
  62. package/dist/init.d.ts.map +1 -1
  63. package/dist/init.js +76 -140
  64. package/dist/init.js.map +1 -1
  65. package/dist/ir/extensions/queries.d.ts +6 -6
  66. package/dist/ir/extensions/queries.d.ts.map +1 -1
  67. package/dist/ir/extensions/queries.js +6 -4
  68. package/dist/ir/extensions/queries.js.map +1 -1
  69. package/dist/ir/extensions/schema-builder.d.ts.map +1 -1
  70. package/dist/ir/extensions/schema-builder.js.map +1 -1
  71. package/dist/ir/index.d.ts.map +1 -1
  72. package/dist/ir/index.js.map +1 -1
  73. package/dist/ir/relation-graph.d.ts.map +1 -1
  74. package/dist/ir/relation-graph.js +8 -8
  75. package/dist/ir/relation-graph.js.map +1 -1
  76. package/dist/ir/semantic-ir.d.ts +38 -0
  77. package/dist/ir/semantic-ir.d.ts.map +1 -1
  78. package/dist/ir/semantic-ir.js +50 -2
  79. package/dist/ir/semantic-ir.js.map +1 -1
  80. package/dist/ir/smart-tags.d.ts.map +1 -1
  81. package/dist/ir/smart-tags.js.map +1 -1
  82. package/dist/lib/field-utils.d.ts.map +1 -1
  83. package/dist/lib/field-utils.js +7 -7
  84. package/dist/lib/field-utils.js.map +1 -1
  85. package/dist/lib/join-graph.d.ts +95 -0
  86. package/dist/lib/join-graph.d.ts.map +1 -0
  87. package/dist/lib/join-graph.js +305 -0
  88. package/dist/lib/join-graph.js.map +1 -0
  89. package/dist/lib/picker.d.ts +60 -0
  90. package/dist/lib/picker.d.ts.map +1 -0
  91. package/dist/lib/picker.js +325 -0
  92. package/dist/lib/picker.js.map +1 -0
  93. package/dist/plugins/arktype.d.ts +20 -24
  94. package/dist/plugins/arktype.d.ts.map +1 -1
  95. package/dist/plugins/arktype.js +462 -386
  96. package/dist/plugins/arktype.js.map +1 -1
  97. package/dist/plugins/effect/http.d.ts +7 -0
  98. package/dist/plugins/effect/http.d.ts.map +1 -0
  99. package/dist/plugins/effect/http.js +460 -0
  100. package/dist/plugins/effect/http.js.map +1 -0
  101. package/dist/plugins/effect/index.d.ts +22 -0
  102. package/dist/plugins/effect/index.d.ts.map +1 -0
  103. package/dist/plugins/effect/index.js +65 -0
  104. package/dist/plugins/effect/index.js.map +1 -0
  105. package/dist/plugins/effect/models.d.ts +6 -0
  106. package/dist/plugins/effect/models.d.ts.map +1 -0
  107. package/dist/plugins/effect/models.js +116 -0
  108. package/dist/plugins/effect/models.js.map +1 -0
  109. package/dist/plugins/effect/repos.d.ts +21 -0
  110. package/dist/plugins/effect/repos.d.ts.map +1 -0
  111. package/dist/plugins/effect/repos.js +131 -0
  112. package/dist/plugins/effect/repos.js.map +1 -0
  113. package/dist/plugins/effect/schemas.d.ts +7 -0
  114. package/dist/plugins/effect/schemas.d.ts.map +1 -0
  115. package/dist/plugins/effect/schemas.js +75 -0
  116. package/dist/plugins/effect/schemas.js.map +1 -0
  117. package/dist/plugins/effect/shared.d.ts +116 -0
  118. package/dist/plugins/effect/shared.d.ts.map +1 -0
  119. package/dist/plugins/effect/shared.js +164 -0
  120. package/dist/plugins/effect/shared.js.map +1 -0
  121. package/dist/plugins/http-elysia.d.ts +20 -27
  122. package/dist/plugins/http-elysia.d.ts.map +1 -1
  123. package/dist/plugins/http-elysia.js +350 -475
  124. package/dist/plugins/http-elysia.js.map +1 -1
  125. package/dist/plugins/http-express.d.ts +20 -31
  126. package/dist/plugins/http-express.d.ts.map +1 -1
  127. package/dist/plugins/http-express.js +281 -268
  128. package/dist/plugins/http-express.js.map +1 -1
  129. package/dist/plugins/http-hono.d.ts +17 -33
  130. package/dist/plugins/http-hono.d.ts.map +1 -1
  131. package/dist/plugins/http-hono.js +317 -341
  132. package/dist/plugins/http-hono.js.map +1 -1
  133. package/dist/plugins/http-orpc.d.ts +34 -33
  134. package/dist/plugins/http-orpc.d.ts.map +1 -1
  135. package/dist/plugins/http-orpc.js +345 -257
  136. package/dist/plugins/http-orpc.js.map +1 -1
  137. package/dist/plugins/http-trpc.d.ts +33 -35
  138. package/dist/plugins/http-trpc.d.ts.map +1 -1
  139. package/dist/plugins/http-trpc.js +337 -241
  140. package/dist/plugins/http-trpc.js.map +1 -1
  141. package/dist/plugins/kysely.d.ts +54 -59
  142. package/dist/plugins/kysely.d.ts.map +1 -1
  143. package/dist/plugins/kysely.js +826 -687
  144. package/dist/plugins/kysely.js.map +1 -1
  145. package/dist/plugins/sql-queries.d.ts +38 -44
  146. package/dist/plugins/sql-queries.d.ts.map +1 -1
  147. package/dist/plugins/sql-queries.js +497 -897
  148. package/dist/plugins/sql-queries.js.map +1 -1
  149. package/dist/plugins/types.d.ts +12 -20
  150. package/dist/plugins/types.d.ts.map +1 -1
  151. package/dist/plugins/types.js +84 -227
  152. package/dist/plugins/types.js.map +1 -1
  153. package/dist/plugins/valibot.d.ts +7 -44
  154. package/dist/plugins/valibot.d.ts.map +1 -1
  155. package/dist/plugins/valibot.js +376 -382
  156. package/dist/plugins/valibot.js.map +1 -1
  157. package/dist/plugins/zod.d.ts +20 -24
  158. package/dist/plugins/zod.d.ts.map +1 -1
  159. package/dist/plugins/zod.js +370 -367
  160. package/dist/plugins/zod.js.map +1 -1
  161. package/dist/runtime/emit.d.ts +64 -0
  162. package/dist/runtime/emit.d.ts.map +1 -0
  163. package/dist/runtime/emit.js +445 -0
  164. package/dist/runtime/emit.js.map +1 -0
  165. package/dist/runtime/errors.d.ts +36 -0
  166. package/dist/runtime/errors.d.ts.map +1 -0
  167. package/dist/runtime/errors.js +29 -0
  168. package/dist/runtime/errors.js.map +1 -0
  169. package/dist/runtime/file-assignment.d.ts +161 -0
  170. package/dist/runtime/file-assignment.d.ts.map +1 -0
  171. package/dist/runtime/file-assignment.js +195 -0
  172. package/dist/runtime/file-assignment.js.map +1 -0
  173. package/dist/runtime/orchestrator.d.ts +62 -0
  174. package/dist/runtime/orchestrator.d.ts.map +1 -0
  175. package/dist/runtime/orchestrator.js +99 -0
  176. package/dist/runtime/orchestrator.js.map +1 -0
  177. package/dist/runtime/registry.d.ts +268 -0
  178. package/dist/runtime/registry.d.ts.map +1 -0
  179. package/dist/runtime/registry.js +436 -0
  180. package/dist/runtime/registry.js.map +1 -0
  181. package/dist/runtime/types.d.ts +182 -0
  182. package/dist/runtime/types.d.ts.map +1 -0
  183. package/dist/runtime/types.js +2 -0
  184. package/dist/runtime/types.js.map +1 -0
  185. package/dist/runtime/validation.d.ts +41 -0
  186. package/dist/runtime/validation.d.ts.map +1 -0
  187. package/dist/runtime/validation.js +70 -0
  188. package/dist/runtime/validation.js.map +1 -0
  189. package/dist/services/config-loader.d.ts.map +1 -1
  190. package/dist/services/config-loader.js +15 -6
  191. package/dist/services/config-loader.js.map +1 -1
  192. package/dist/services/config.d.ts +55 -25
  193. package/dist/services/config.d.ts.map +1 -1
  194. package/dist/services/config.js +60 -34
  195. package/dist/services/config.js.map +1 -1
  196. package/dist/services/file-writer.d.ts +3 -3
  197. package/dist/services/file-writer.d.ts.map +1 -1
  198. package/dist/services/file-writer.js +6 -8
  199. package/dist/services/file-writer.js.map +1 -1
  200. package/dist/services/inflection.d.ts +126 -27
  201. package/dist/services/inflection.d.ts.map +1 -1
  202. package/dist/services/inflection.js +300 -72
  203. package/dist/services/inflection.js.map +1 -1
  204. package/dist/services/introspection.d.ts.map +1 -1
  205. package/dist/services/introspection.js +6 -6
  206. package/dist/services/introspection.js.map +1 -1
  207. package/dist/services/ir-builder.d.ts.map +1 -1
  208. package/dist/services/ir-builder.js +73 -77
  209. package/dist/services/ir-builder.js.map +1 -1
  210. package/dist/services/ir.d.ts.map +1 -1
  211. package/dist/services/ir.js.map +1 -1
  212. package/dist/services/pg-types.d.ts.map +1 -1
  213. package/dist/services/pg-types.js +3 -3
  214. package/dist/services/pg-types.js.map +1 -1
  215. package/dist/services/smart-tags-parser.d.ts.map +1 -1
  216. package/dist/services/smart-tags-parser.js +4 -4
  217. package/dist/services/smart-tags-parser.js.map +1 -1
  218. package/dist/services/type-hints.d.ts.map +1 -1
  219. package/dist/services/type-hints.js +1 -1
  220. package/dist/services/type-hints.js.map +1 -1
  221. package/dist/services/user-module-parser.d.ts +46 -0
  222. package/dist/services/user-module-parser.d.ts.map +1 -0
  223. package/dist/services/user-module-parser.js +181 -0
  224. package/dist/services/user-module-parser.js.map +1 -0
  225. package/dist/shared/converters.d.ts +60 -0
  226. package/dist/shared/converters.d.ts.map +1 -0
  227. package/dist/shared/converters.js +168 -0
  228. package/dist/shared/converters.js.map +1 -0
  229. package/dist/shared/query-types.d.ts +95 -0
  230. package/dist/shared/query-types.d.ts.map +1 -0
  231. package/dist/shared/query-types.js +9 -0
  232. package/dist/shared/query-types.js.map +1 -0
  233. package/dist/testing.d.ts +125 -37
  234. package/dist/testing.d.ts.map +1 -1
  235. package/dist/testing.js +134 -42
  236. package/dist/testing.js.map +1 -1
  237. package/dist/user-module.d.ts +86 -0
  238. package/dist/user-module.d.ts.map +1 -0
  239. package/dist/user-module.js +55 -0
  240. package/dist/user-module.js.map +1 -0
  241. package/package.json +10 -6
  242. package/dist/lib/conjure.d.ts.map +0 -1
  243. package/dist/lib/conjure.js.map +0 -1
  244. package/dist/lib/hex.d.ts +0 -119
  245. package/dist/lib/hex.d.ts.map +0 -1
  246. package/dist/lib/hex.js +0 -188
  247. package/dist/lib/hex.js.map +0 -1
  248. package/dist/plugins/effect.d.ts +0 -53
  249. package/dist/plugins/effect.d.ts.map +0 -1
  250. package/dist/plugins/effect.js +0 -1074
  251. package/dist/plugins/effect.js.map +0 -1
  252. package/dist/plugins/kysely/queries.d.ts +0 -92
  253. package/dist/plugins/kysely/queries.d.ts.map +0 -1
  254. package/dist/plugins/kysely/queries.js +0 -1169
  255. package/dist/plugins/kysely/queries.js.map +0 -1
  256. package/dist/plugins/kysely/shared.d.ts +0 -59
  257. package/dist/plugins/kysely/shared.d.ts.map +0 -1
  258. package/dist/plugins/kysely/shared.js +0 -247
  259. package/dist/plugins/kysely/shared.js.map +0 -1
  260. package/dist/plugins/kysely/types.d.ts +0 -22
  261. package/dist/plugins/kysely/types.d.ts.map +0 -1
  262. package/dist/plugins/kysely/types.js +0 -428
  263. package/dist/plugins/kysely/types.js.map +0 -1
  264. package/dist/services/artifact-store.d.ts +0 -65
  265. package/dist/services/artifact-store.d.ts.map +0 -1
  266. package/dist/services/artifact-store.js +0 -57
  267. package/dist/services/artifact-store.js.map +0 -1
  268. package/dist/services/core-providers.d.ts +0 -15
  269. package/dist/services/core-providers.d.ts.map +0 -1
  270. package/dist/services/core-providers.js +0 -23
  271. package/dist/services/core-providers.js.map +0 -1
  272. package/dist/services/emissions.d.ts +0 -103
  273. package/dist/services/emissions.d.ts.map +0 -1
  274. package/dist/services/emissions.js +0 -241
  275. package/dist/services/emissions.js.map +0 -1
  276. package/dist/services/execution.d.ts +0 -35
  277. package/dist/services/execution.d.ts.map +0 -1
  278. package/dist/services/execution.js +0 -86
  279. package/dist/services/execution.js.map +0 -1
  280. package/dist/services/file-builder.d.ts +0 -85
  281. package/dist/services/file-builder.d.ts.map +0 -1
  282. package/dist/services/file-builder.js +0 -112
  283. package/dist/services/file-builder.js.map +0 -1
  284. package/dist/services/plugin-meta.d.ts +0 -33
  285. package/dist/services/plugin-meta.d.ts.map +0 -1
  286. package/dist/services/plugin-meta.js +0 -24
  287. package/dist/services/plugin-meta.js.map +0 -1
  288. package/dist/services/plugin-runner.d.ts +0 -42
  289. package/dist/services/plugin-runner.d.ts.map +0 -1
  290. package/dist/services/plugin-runner.js +0 -84
  291. package/dist/services/plugin-runner.js.map +0 -1
  292. package/dist/services/plugin.d.ts +0 -421
  293. package/dist/services/plugin.d.ts.map +0 -1
  294. package/dist/services/plugin.js +0 -197
  295. package/dist/services/plugin.js.map +0 -1
  296. package/dist/services/resolution.d.ts +0 -38
  297. package/dist/services/resolution.d.ts.map +0 -1
  298. package/dist/services/resolution.js +0 -242
  299. package/dist/services/resolution.js.map +0 -1
  300. package/dist/services/service-registry.d.ts +0 -74
  301. package/dist/services/service-registry.d.ts.map +0 -1
  302. package/dist/services/service-registry.js +0 -61
  303. package/dist/services/service-registry.js.map +0 -1
  304. package/dist/services/symbols.d.ts +0 -144
  305. package/dist/services/symbols.d.ts.map +0 -1
  306. package/dist/services/symbols.js +0 -144
  307. package/dist/services/symbols.js.map +0 -1
@@ -1,401 +1,404 @@
1
1
  /**
2
- * Zod Plugin - Generate Zod schemas for entities
2
+ * Zod Plugin - Generates Zod schemas for entities
3
3
  *
4
4
  * Generates Zod schemas for Row, Insert, Update, and Patch shapes,
5
- * with inferred TypeScript types.
6
- */
7
- import { Array as Arr, Option, pipe, Schema as S } from "effect";
8
- import { definePlugin } from "../services/plugin.js";
9
- import { findEnumByPgName, TsType } from "../services/pg-types.js";
10
- import { SCHEMA_BUILDER_KIND, } from "../ir/extensions/schema-builder.js";
11
- import { getTableEntities, getEnumEntities, getCompositeEntities } from "../ir/semantic-ir.js";
12
- import { conjure } from "../lib/conjure.js";
13
- import { isUuidType, isDateType, isEnumType, getPgTypeName, resolveFieldType, } from "../lib/field-utils.js";
14
- const { ts, exp, obj } = conjure;
15
- const ZodConfigSchema = S.Struct({
16
- outputDir: S.optionalWith(S.String, { default: () => "zod" }),
17
- exportTypes: S.optionalWith(S.Boolean, { default: () => true }),
18
- enumStyle: S.optionalWith(S.Union(S.Literal("strings"), S.Literal("enum")), { default: () => "strings" }),
19
- typeReferences: S.optionalWith(S.Union(S.Literal("inline"), S.Literal("separate")), { default: () => "separate" }),
20
- });
21
- // ============================================================================
22
- // Zod Schema Builders (pure functions)
23
- // ============================================================================
24
- /**
25
- * Build z.<method>() and chain additional methods
26
- */
27
- const buildZodChain = (baseMethod, chainMethods) => chainMethods
28
- .reduce((chain, method) => chain.method(method), conjure.id("z").method(baseMethod))
29
- .build();
30
- /**
31
- * Build z.enum([...values])
5
+ * with optional inferred TypeScript types.
6
+ *
7
+ * Capabilities provided:
8
+ * - `schema:zod:EntityName` for each table entity (Row schema)
9
+ * - `schema:zod:EntityName:insert` for Insert shape
10
+ * - `schema:zod:EntityName:update` for Update shape
11
+ * - `schema:zod:EnumName` for enum entities
32
12
  */
33
- const buildZodEnum = (values) => conjure
34
- .id("z")
35
- .method("enum", [conjure.arr(...values.map(v => conjure.str(v))).build()])
36
- .build();
13
+ import { Effect, Schema as S } from "effect";
14
+ import { normalizeFileNaming } from "../runtime/file-assignment.js";
15
+ import { SymbolRegistry } from "../runtime/registry.js";
16
+ import { IR } from "../services/ir.js";
17
+ import { isTableEntity, isEnumEntity, } from "../ir/semantic-ir.js";
18
+ import { conjure, cast } from "../conjure/index.js";
37
19
  /**
38
- * Build z.array(<inner>)
20
+ * Creates a consume callback for Zod schemas.
21
+ * Generates: `SchemaName.parse(input)`
22
+ *
23
+ * @param schemaName - The name of the Zod schema
24
+ * @returns A function that generates parse AST for an input expression
39
25
  */
40
- const buildZodArray = (inner) => conjure.id("z").method("array", [inner]).build();
26
+ function createZodConsumeCallback(schemaName) {
27
+ return (input) => {
28
+ return conjure.id(schemaName).method("parse", [cast.toExpr(input)]).build();
29
+ };
30
+ }
41
31
  /**
42
- * Add method calls to an existing expression
32
+ * Zod SchemaBuilder implementation.
33
+ * Builds Zod schemas for path/query parameters.
43
34
  */
44
- const chainZodMethods = (expr, methods) => methods.reduce((chain, method) => chain.method(method), conjure.chain(expr)).build();
35
+ const zodSchemaBuilder = {
36
+ build(request) {
37
+ if (request.params.length === 0) {
38
+ return undefined;
39
+ }
40
+ // Build z.object({ field: z.type(), ... })
41
+ let objBuilder = conjure.obj();
42
+ for (const param of request.params) {
43
+ const zodType = paramToZodType(param);
44
+ objBuilder = objBuilder.prop(param.name, zodType);
45
+ }
46
+ const ast = conjure.id("z").method("object", [objBuilder.build()]).build();
47
+ return {
48
+ ast,
49
+ importSpec: { from: "zod", names: ["z"] },
50
+ };
51
+ },
52
+ };
45
53
  /**
46
- * Map TypeScript type to Zod method name
54
+ * Convert a QueryMethodParam to a Zod type expression.
47
55
  */
48
- const tsTypeToZodMethod = (tsType) => {
49
- switch (tsType) {
50
- case TsType.String:
51
- return "string";
52
- case TsType.Number:
53
- return "number";
54
- case TsType.Boolean:
55
- return "boolean";
56
- case TsType.BigInt:
57
- return "bigint";
58
- case TsType.Date:
59
- return "date";
60
- case TsType.Buffer:
61
- case TsType.Unknown:
56
+ function paramToZodType(param) {
57
+ const baseType = param.type.replace(/\[\]$/, "").replace(/\?$/, "").toLowerCase();
58
+ let zodChain = conjure.id("z");
59
+ switch (baseType) {
60
+ case "number":
61
+ case "int":
62
+ case "integer":
63
+ case "float":
64
+ case "double":
65
+ zodChain = zodChain.prop("coerce").method("number");
66
+ break;
67
+ case "boolean":
68
+ case "bool":
69
+ zodChain = zodChain.method("boolean");
70
+ break;
71
+ case "date":
72
+ zodChain = zodChain.prop("coerce").method("date");
73
+ break;
74
+ case "string":
62
75
  default:
63
- return "unknown";
76
+ zodChain = zodChain.method("string");
77
+ break;
64
78
  }
65
- };
66
- // ============================================================================
67
- // Field → Zod Schema Resolution
68
- // ============================================================================
69
- /**
70
- * Resolve a field to its Zod schema expression.
71
- *
72
- * Order of resolution (first match wins):
73
- * 1. UUID types → z.string().uuid()
74
- * 2. Date types → z.coerce.date()
75
- * 3. Enum types → z.enum([...]) or reference
76
- * 4. Fallback to resolved TypeScript type
77
- *
78
- * Then wraps with array/nullable/optional as needed.
79
- */
80
- const resolveFieldZodSchema = (field, ctx) => {
81
- // Get the base schema expression
82
- let baseSchema;
83
- // 1. UUID types get special treatment
84
- if (isUuidType(field)) {
85
- baseSchema = buildZodChain("string", ["uuid"]);
79
+ if (!param.required) {
80
+ zodChain = zodChain.method("optional");
86
81
  }
87
- // 2. Date types use coercion for flexibility
88
- else if (isDateType(field)) {
89
- baseSchema = conjure.id("z").prop("coerce").method("date").build();
82
+ return zodChain.build();
83
+ }
84
+ const b = conjure.b;
85
+ const ZodSchemaConfig = S.Struct({
86
+ exportTypes: S.optionalWith(S.Boolean, { default: () => true }),
87
+ });
88
+ function toExpr(node) {
89
+ return node;
90
+ }
91
+ function toStmt(node) {
92
+ return node;
93
+ }
94
+ // =============================================================================
95
+ // PostgreSQL Type to Zod Schema Mapping
96
+ // =============================================================================
97
+ const PG_STRING_TYPES = new Set([
98
+ "uuid",
99
+ "text",
100
+ "varchar",
101
+ "char",
102
+ "character",
103
+ "name",
104
+ "bpchar",
105
+ "citext",
106
+ ]);
107
+ const PG_NUMBER_TYPES = new Set([
108
+ "int2",
109
+ "int4",
110
+ "int8",
111
+ "integer",
112
+ "smallint",
113
+ "bigint",
114
+ "numeric",
115
+ "decimal",
116
+ "real",
117
+ "float4",
118
+ "float8",
119
+ "double",
120
+ ]);
121
+ const PG_BOOLEAN_TYPES = new Set(["bool", "boolean"]);
122
+ const PG_DATE_TYPES = new Set(["timestamp", "timestamptz", "date", "time", "timetz"]);
123
+ const PG_JSON_TYPES = new Set(["json", "jsonb"]);
124
+ function fieldToZodMapping(field, enums) {
125
+ const pgType = field.pgAttribute.getType();
126
+ if (!pgType) {
127
+ return { kind: "schema", schema: conjure.id("z").method("unknown").build() };
90
128
  }
91
- // 3. Enum types
92
- else if (isEnumType(field)) {
93
- const pgTypeName = getPgTypeName(field);
94
- const enumDef = pgTypeName
95
- ? pipe(findEnumByPgName(ctx.enums, pgTypeName), Option.getOrUndefined)
96
- : undefined;
97
- if (enumDef) {
98
- if (ctx.typeReferences === "inline") {
99
- // Inline the enum values
100
- baseSchema = buildZodEnum(enumDef.values);
101
- }
102
- else {
103
- // Reference the separate enum schema
104
- baseSchema = conjure.id(enumDef.name).build();
105
- }
106
- }
107
- else {
108
- baseSchema = buildZodChain("unknown", []);
109
- }
129
+ // For arrays, use element type; for domains, use base type; otherwise use pgType
130
+ let typeName;
131
+ let typeInfo;
132
+ if (pgType.typcategory === "A") {
133
+ // Array type - use element type name
134
+ typeName = field.elementTypeName ?? "unknown";
135
+ typeInfo = pgType;
136
+ }
137
+ else if (pgType.typtype === "d" && field.domainBaseType) {
138
+ // Domain type - resolve to underlying base type
139
+ typeName = field.domainBaseType.typeName;
140
+ typeInfo = { typcategory: field.domainBaseType.category };
110
141
  }
111
- // 4. Fallback to resolved TypeScript type
112
142
  else {
113
- const resolved = resolveFieldType(field, ctx.enums, ctx.extensions);
114
- const zodMethod = tsTypeToZodMethod(resolved.tsType);
115
- baseSchema = buildZodChain(zodMethod, []);
143
+ typeName = pgType.typname;
144
+ typeInfo = pgType;
145
+ }
146
+ const baseResult = baseTypeToZodMapping(typeName, typeInfo, enums);
147
+ // For enum references, return as-is (modifiers applied in shapeToZodObject)
148
+ if (baseResult.kind === "enumRef") {
149
+ return baseResult;
116
150
  }
117
- // Wrap with array if needed
151
+ let schema = baseResult.schema;
118
152
  if (field.isArray) {
119
- baseSchema = buildZodArray(baseSchema);
153
+ schema = conjure.chain(schema).method("array").build();
120
154
  }
121
- // Collect modifiers to chain
122
- const modifiers = [];
155
+ const methods = [];
123
156
  if (field.nullable)
124
- modifiers.push("nullable");
157
+ methods.push("nullable");
125
158
  if (field.optional)
126
- modifiers.push("optional");
127
- // Apply modifiers
128
- return modifiers.length > 0 ? chainZodMethods(baseSchema, modifiers) : baseSchema;
129
- };
130
- // ============================================================================
131
- // Statement Generators
132
- // ============================================================================
133
- /**
134
- * Build z.object({...}) expression from shape fields
135
- */
136
- const buildShapeZodObject = (shape, ctx) => {
137
- const objBuilder = shape.fields.reduce((builder, field) => builder.prop(field.name, resolveFieldZodSchema(field, ctx)), obj());
138
- return conjure.id("z").method("object", [objBuilder.build()]).build();
139
- };
140
- /**
141
- * Generate schema const + optional inferred type for a shape
142
- */
143
- const generateShapeStatements = (shape, entityName, shapeKind, ctx, exportTypes) => {
144
- const schemaSymbolCtx = { capability: "schemas", entity: entityName, shape: shapeKind };
145
- const schemaExpr = buildShapeZodObject(shape, ctx);
146
- const schemaStatement = exp.const(shape.name, schemaSymbolCtx, schemaExpr);
147
- if (!exportTypes) {
148
- return [schemaStatement];
159
+ methods.push("optional");
160
+ for (const method of methods) {
161
+ schema = conjure.chain(schema).method(method).build();
149
162
  }
150
- // Generate: export type ShapeName = z.infer<typeof ShapeName>
151
- // Register under "types" capability so other plugins can import
152
- const typeSymbolCtx = { capability: "types", entity: entityName, shape: shapeKind };
153
- const inferType = ts.qualifiedRef("z", "infer", [ts.typeof(shape.name)]);
154
- const typeStatement = exp.type(shape.name, typeSymbolCtx, inferType);
155
- return [schemaStatement, typeStatement];
156
- };
157
- /**
158
- * Collect all defined shapes from an entity as [kind, shape] pairs
159
- */
160
- const collectShapes = (entity) => [
161
- ["row", entity.shapes.row],
162
- ["insert", entity.shapes.insert],
163
- ["update", entity.shapes.update],
164
- ].filter((entry) => entry[1] != null);
165
- /**
166
- * Generate all statements for an entity's shapes
167
- */
168
- const generateEntityStatements = (entity, ctx, exportTypes) => collectShapes(entity).flatMap(([kind, shape]) => generateShapeStatements(shape, entity.name, kind, ctx, exportTypes));
169
- // ============================================================================
170
- // Composite Type Generation
171
- // ============================================================================
172
- /**
173
- * Build z.object({...}) expression from composite fields
174
- */
175
- const buildCompositeZodObject = (composite, ctx) => {
176
- const objBuilder = composite.fields.reduce((builder, field) => builder.prop(field.name, resolveFieldZodSchema(field, ctx)), obj());
177
- return conjure.id("z").method("object", [objBuilder.build()]).build();
178
- };
179
- /**
180
- * Generate schema const + optional inferred type for a composite type
181
- */
182
- const generateCompositeStatements = (composite, ctx, exportTypes) => {
183
- const schemaSymbolCtx = { capability: "schemas", entity: composite.name };
184
- const schemaExpr = buildCompositeZodObject(composite, ctx);
185
- const schemaStatement = exp.const(composite.name, schemaSymbolCtx, schemaExpr);
186
- if (!exportTypes) {
187
- return [schemaStatement];
163
+ return { kind: "schema", schema };
164
+ }
165
+ function baseTypeToZodMapping(typeName, pgType, enums) {
166
+ const normalized = typeName.toLowerCase();
167
+ if (PG_STRING_TYPES.has(normalized)) {
168
+ if (normalized === "uuid") {
169
+ return { kind: "schema", schema: conjure.id("z").method("uuid").build() };
170
+ }
171
+ // citext is case-insensitive text - just treat as regular string
172
+ // (Zod doesn't have a built-in case-insensitive string validator)
173
+ return { kind: "schema", schema: conjure.id("z").method("string").build() };
188
174
  }
189
- // Generate: export type CompositeName = z.infer<typeof CompositeName>
190
- // Register under "types" capability so other plugins can import
191
- const typeSymbolCtx = { capability: "types", entity: composite.name };
192
- const inferType = ts.qualifiedRef("z", "infer", [ts.typeof(composite.name)]);
193
- const typeStatement = exp.type(composite.name, typeSymbolCtx, inferType);
194
- return [schemaStatement, typeStatement];
195
- };
196
- // ============================================================================
197
- // Enum Generation
198
- // ============================================================================
199
- /**
200
- * Generate enum schema statement: export const EnumName = z.enum(['a', 'b', ...])
201
- * or for native enums: export enum EnumName { A = 'a', ... } + schema
202
- */
203
- const generateEnumStatement = (enumEntity, enumStyle, exportTypes) => {
204
- const schemaSymbolCtx = { capability: "schemas", entity: enumEntity.name };
205
- if (enumStyle === "enum") {
206
- // Generate: export enum EnumName { A = 'a', B = 'b', ... }
207
- // Then: export const EnumNameSchema = z.nativeEnum(EnumName)
208
- const enumStatement = exp.tsEnum(enumEntity.name, { capability: "types", entity: enumEntity.name }, enumEntity.values);
209
- const schemaName = `${enumEntity.name}Schema`;
210
- const schemaExpr = conjure
211
- .id("z")
212
- .method("nativeEnum", [conjure.id(enumEntity.name).build()])
213
- .build();
214
- const schemaStatement = exp.const(schemaName, schemaSymbolCtx, schemaExpr);
215
- return [enumStatement, schemaStatement];
175
+ if (PG_NUMBER_TYPES.has(normalized)) {
176
+ return { kind: "schema", schema: conjure.id("z").method("number").build() };
216
177
  }
217
- // strings style: export const EnumName = z.enum(['a', 'b', ...])
218
- const schemaExpr = buildZodEnum(enumEntity.values);
219
- const schemaStatement = exp.const(enumEntity.name, schemaSymbolCtx, schemaExpr);
220
- if (!exportTypes) {
221
- return [schemaStatement];
178
+ if (PG_BOOLEAN_TYPES.has(normalized)) {
179
+ return { kind: "schema", schema: conjure.id("z").method("boolean").build() };
222
180
  }
223
- // Generate: export type EnumName = z.infer<typeof EnumName>
224
- // Register under "types" capability so other plugins can import
225
- const typeSymbolCtx = { capability: "types", entity: enumEntity.name };
226
- const inferType = ts.qualifiedRef("z", "infer", [ts.typeof(enumEntity.name)]);
227
- const typeStatement = exp.type(enumEntity.name, typeSymbolCtx, inferType);
228
- return [schemaStatement, typeStatement];
229
- };
230
- /** Collect enum names used by fields */
231
- const collectUsedEnums = (fields, enums) => {
232
- const enumNames = fields.filter(isEnumType).flatMap(field => {
233
- const pgTypeName = getPgTypeName(field);
234
- if (!pgTypeName)
235
- return [];
236
- return pipe(findEnumByPgName(enums, pgTypeName), Option.map(e => e.name), Option.toArray);
237
- });
238
- return new Set(enumNames);
239
- };
240
- /** Build import refs for used enums */
241
- const buildEnumImports = (usedEnums) => Arr.fromIterable(usedEnums).map(enumName => ({
242
- kind: "symbol",
243
- ref: { capability: "schemas", entity: enumName },
244
- }));
245
- // ============================================================================
246
- // Schema Builder Service
247
- // ============================================================================
248
- /**
249
- * Build Zod schema expression for a single param.
250
- * Uses z.coerce for numeric types since URL params are always strings.
251
- */
252
- const buildParamFieldSchema = (param) => {
253
- const tsType = param.type.toLowerCase();
254
- let fieldSchema;
255
- switch (tsType) {
256
- case "number":
257
- // z.coerce.number() - converts string "10" to number 10
258
- fieldSchema = conjure.id("z").prop("coerce").method("number").build();
259
- break;
260
- case "boolean":
261
- // z.coerce.boolean() - converts "true"/"false" strings
262
- fieldSchema = conjure.id("z").prop("coerce").method("boolean").build();
263
- break;
264
- case "bigint":
265
- // z.coerce.bigint()
266
- fieldSchema = conjure.id("z").prop("coerce").method("bigint").build();
267
- break;
268
- case "date":
269
- // z.coerce.date() - converts ISO strings to Date objects
270
- fieldSchema = conjure.id("z").prop("coerce").method("date").build();
271
- break;
272
- case "string":
273
- default:
274
- // z.string() - no coercion needed
275
- fieldSchema = buildZodChain("string", []);
276
- break;
181
+ if (PG_DATE_TYPES.has(normalized)) {
182
+ return { kind: "schema", schema: conjure.id("z").method("coerce").method("date").build() };
277
183
  }
278
- // Add .optional() for non-required params
279
- if (!param.required) {
280
- fieldSchema = chainZodMethods(fieldSchema, ["optional"]);
184
+ if (PG_JSON_TYPES.has(normalized)) {
185
+ return { kind: "schema", schema: conjure.id("z").method("unknown").build() };
281
186
  }
282
- return fieldSchema;
283
- };
284
- /**
285
- * Build z.object({ ... }) expression from QueryMethodParam[].
286
- * For path/query parameter validation in HTTP handlers.
287
- */
288
- const buildParamZodObject = (params) => {
289
- const objBuilder = params.reduce((builder, param) => builder.prop(param.name, buildParamFieldSchema(param)), obj());
290
- return conjure.id("z").method("object", [objBuilder.build()]).build();
291
- };
292
- /**
293
- * Create a SchemaBuilder implementation for Zod.
294
- */
295
- const createZodSchemaBuilder = () => ({
296
- build: (request) => {
297
- if (request.params.length === 0) {
298
- return undefined;
187
+ if (pgType.typtype === "e" || pgType.typcategory === "E") {
188
+ const enumEntity = enums.find(e => e.pgType.typname === typeName);
189
+ if (enumEntity) {
190
+ // Return reference to the enum schema instead of inlining
191
+ return { kind: "enumRef", enumRef: enumEntity.name };
299
192
  }
300
- const ast = buildParamZodObject(request.params);
301
- return {
302
- ast,
303
- importSpec: {
304
- names: ["z"],
305
- from: "zod",
306
- },
307
- };
308
- },
309
- });
310
- // ============================================================================
311
- // Provider Definition
312
- // ============================================================================
313
- /**
314
- * Create a zod provider that generates Zod schemas.
315
- *
316
- * @example
317
- * ```typescript
318
- * import { zod } from "pg-sourcerer"
319
- *
320
- * export default defineConfig({
321
- * plugins: [
322
- * zod(),
323
- * zod({ outputDir: "schemas", exportTypes: false }),
324
- * ],
325
- * })
326
- * ```
327
- */
328
- export function zod(config = {}) {
329
- const parsed = S.decodeUnknownSync(ZodConfigSchema)(config);
330
- return definePlugin({
193
+ return { kind: "schema", schema: conjure.id("z").method("unknown").build() };
194
+ }
195
+ return { kind: "schema", schema: conjure.id("z").method("unknown").build() };
196
+ }
197
+ // =============================================================================
198
+ // Shape to Zod Object
199
+ // =============================================================================
200
+ function shapeToZodObject(shape, enums, registry) {
201
+ const properties = shape.fields.map(field => {
202
+ const mapping = fieldToZodMapping(field, enums);
203
+ let value;
204
+ if (mapping.kind === "enumRef") {
205
+ // Get handle and track cross-reference
206
+ const enumHandle = registry.import(`schema:zod:${mapping.enumRef}`);
207
+ value = enumHandle.ref();
208
+ // Apply modifiers for enum references
209
+ if (field.isArray) {
210
+ value = conjure.chain(value).method("array").build();
211
+ }
212
+ if (field.nullable) {
213
+ value = conjure.chain(value).method("nullable").build();
214
+ }
215
+ if (field.optional) {
216
+ value = conjure.chain(value).method("optional").build();
217
+ }
218
+ }
219
+ else {
220
+ value = mapping.schema;
221
+ }
222
+ return b.objectProperty(b.identifier(field.name), toExpr(value));
223
+ });
224
+ const objExpr = b.objectExpression(properties);
225
+ const zObject = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("object")), [
226
+ objExpr,
227
+ ]);
228
+ return zObject;
229
+ }
230
+ // =============================================================================
231
+ // Zod Plugin Definition
232
+ // =============================================================================
233
+ function getShapeDeclarations(entity) {
234
+ const declarations = [];
235
+ const baseEntityName = entity.name;
236
+ // Row shape uses the entity name directly
237
+ declarations.push({
238
+ name: entity.shapes.row.name,
239
+ capability: `schema:zod:${entity.shapes.row.name}`,
240
+ baseEntityName,
241
+ });
242
+ if (entity.shapes.insert) {
243
+ const insertName = entity.shapes.insert.name;
244
+ declarations.push({
245
+ name: insertName,
246
+ capability: `schema:zod:${insertName}`,
247
+ baseEntityName,
248
+ });
249
+ declarations.push({
250
+ name: insertName,
251
+ capability: `schema:zod:${insertName}:type`,
252
+ baseEntityName,
253
+ });
254
+ }
255
+ if (entity.shapes.update) {
256
+ const updateName = entity.shapes.update.name;
257
+ declarations.push({
258
+ name: updateName,
259
+ capability: `schema:zod:${updateName}`,
260
+ baseEntityName,
261
+ });
262
+ declarations.push({
263
+ name: updateName,
264
+ capability: `schema:zod:${updateName}:type`,
265
+ baseEntityName,
266
+ });
267
+ }
268
+ return declarations;
269
+ }
270
+ export function zod(config) {
271
+ // Parse schema-validated options
272
+ const schemaConfig = S.decodeSync(ZodSchemaConfig)(config ?? {});
273
+ // Resolve file naming
274
+ const resolvedConfig = {
275
+ ...schemaConfig,
276
+ schemasFile: normalizeFileNaming(config?.schemasFile, "schemas.ts"),
277
+ };
278
+ return {
331
279
  name: "zod",
332
- kind: "schemas",
333
- singleton: true,
334
- canProvide: () => true,
335
- provide: (_params, _deps, ctx) => {
336
- const { ir, inflection } = ctx;
337
- const enumEntities = getEnumEntities(ir);
338
- // Register schema-builder service for on-demand param/query schema generation
339
- ctx.registerHandler(SCHEMA_BUILDER_KIND, createZodSchemaBuilder().build);
340
- const fieldCtx = {
341
- enums: enumEntities,
342
- extensions: ir.extensions,
343
- enumStyle: parsed.enumStyle,
344
- typeReferences: parsed.typeReferences,
345
- };
346
- // Helper to build file path
347
- const buildFilePath = (entityName) => `${parsed.outputDir}/${entityName}.ts`;
348
- // Generate separate enum files if configured
349
- if (parsed.typeReferences === "separate") {
350
- enumEntities
351
- .filter(e => e.tags.omit !== true)
352
- .forEach(enumEntity => {
353
- const statements = generateEnumStatement(enumEntity, parsed.enumStyle, parsed.exportTypes);
354
- ctx
355
- .file(buildFilePath(enumEntity.name))
356
- .import({ kind: "package", names: ["z"], from: "zod" })
357
- .ast(conjure.symbolProgram(...statements))
358
- .emit();
359
- });
280
+ provides: ["schema"],
281
+ fileDefaults: [
282
+ {
283
+ pattern: "schema:",
284
+ fileNaming: resolvedConfig.schemasFile,
285
+ },
286
+ ],
287
+ declare: Effect.gen(function* () {
288
+ const ir = yield* IR;
289
+ const declarations = [];
290
+ for (const entity of ir.entities.values()) {
291
+ if (isTableEntity(entity)) {
292
+ // Push declarations directly - they already include baseEntityName
293
+ declarations.push(...getShapeDeclarations(entity));
294
+ }
295
+ else if (isEnumEntity(entity)) {
296
+ declarations.push({
297
+ name: entity.name,
298
+ capability: `schema:zod:${entity.name}`,
299
+ baseEntityName: entity.name,
300
+ });
301
+ declarations.push({
302
+ name: entity.name,
303
+ capability: `schema:zod:${entity.name}:type`,
304
+ baseEntityName: entity.name,
305
+ });
306
+ }
360
307
  }
361
- getTableEntities(ir)
362
- .filter(entity => entity.tags.omit !== true)
363
- .forEach(entity => {
364
- const statements = generateEntityStatements(entity, fieldCtx, parsed.exportTypes);
365
- const entityName = inflection.entityName(entity.pgClass, entity.tags);
366
- // Collect all fields for enum detection
367
- const allFields = [
368
- ...entity.shapes.row.fields,
369
- ...(entity.shapes.insert?.fields ?? []),
370
- ...(entity.shapes.update?.fields ?? []),
371
- ];
372
- const usedEnums = parsed.typeReferences === "separate"
373
- ? collectUsedEnums(allFields, Arr.fromIterable(fieldCtx.enums))
374
- : new Set();
375
- const fileBuilder = ctx
376
- .file(buildFilePath(entityName))
377
- .import({ kind: "package", names: ["z"], from: "zod" });
378
- // Add enum imports when using separate files
379
- buildEnumImports(usedEnums).forEach(ref => fileBuilder.import(ref));
380
- fileBuilder.ast(conjure.symbolProgram(...statements)).emit();
308
+ // Declare the schema builder capability
309
+ declarations.push({
310
+ name: "zodSchemaBuilder",
311
+ capability: "schema:zod:builder",
381
312
  });
382
- // Generate composite type schemas
383
- getCompositeEntities(ir)
384
- .filter(composite => composite.tags.omit !== true)
385
- .forEach(composite => {
386
- const statements = generateCompositeStatements(composite, fieldCtx, parsed.exportTypes);
387
- // Collect enum usage for imports
388
- const usedEnums = parsed.typeReferences === "separate"
389
- ? collectUsedEnums(composite.fields, Arr.fromIterable(fieldCtx.enums))
390
- : new Set();
391
- const fileBuilder = ctx
392
- .file(buildFilePath(composite.name))
393
- .import({ kind: "package", names: ["z"], from: "zod" });
394
- buildEnumImports(usedEnums).forEach(ref => fileBuilder.import(ref));
395
- fileBuilder.ast(conjure.symbolProgram(...statements)).emit();
313
+ return declarations;
314
+ }),
315
+ render: Effect.gen(function* () {
316
+ const ir = yield* IR;
317
+ const registry = yield* SymbolRegistry;
318
+ const enums = [...ir.entities.values()].filter(isEnumEntity);
319
+ const rendered = [];
320
+ for (const entity of ir.entities.values()) {
321
+ if (isTableEntity(entity)) {
322
+ const shapes = [
323
+ entity.shapes.row,
324
+ ];
325
+ if (entity.shapes.insert)
326
+ shapes.push(entity.shapes.insert);
327
+ if (entity.shapes.update)
328
+ shapes.push(entity.shapes.update);
329
+ for (const shape of shapes) {
330
+ const isRow = shape.kind === "row";
331
+ const capability = `schema:zod:${shape.name}`;
332
+ // Scope cross-references to this specific capability
333
+ const schemaNode = registry.forSymbol(capability, () => shapeToZodObject(shape, enums, registry));
334
+ const schemaDecl = conjure.export.const(shape.name, schemaNode);
335
+ rendered.push({
336
+ name: shape.name,
337
+ capability,
338
+ node: schemaDecl,
339
+ exports: "named",
340
+ externalImports: [{ from: "zod", names: ["z"] }],
341
+ metadata: {
342
+ consume: createZodConsumeCallback(shape.name),
343
+ },
344
+ });
345
+ if (resolvedConfig.exportTypes && !isRow) {
346
+ const inferType = conjure.ts.qualifiedRef("z", "infer", [
347
+ conjure.ts.typeof(shape.name),
348
+ ]);
349
+ const typeDecl = conjure.export.type(shape.name, inferType);
350
+ rendered.push({
351
+ name: shape.name,
352
+ capability: `schema:zod:${shape.name}:type`,
353
+ node: typeDecl,
354
+ exports: "named",
355
+ externalImports: [{ from: "zod", names: ["z"] }],
356
+ });
357
+ }
358
+ }
359
+ }
360
+ else if (isEnumEntity(entity)) {
361
+ const schemaNode = conjure
362
+ .id("z")
363
+ .method("enum", [conjure.arr(...entity.values.map(v => conjure.str(v))).build()])
364
+ .build();
365
+ const schemaDecl = conjure.export.const(entity.name, schemaNode);
366
+ const inferType = conjure.ts.qualifiedRef("z", "infer", [conjure.ts.typeof(entity.name)]);
367
+ const typeDecl = conjure.export.type(entity.name, inferType);
368
+ rendered.push({
369
+ name: entity.name,
370
+ capability: `schema:zod:${entity.name}`,
371
+ node: schemaDecl,
372
+ exports: "named",
373
+ externalImports: [{ from: "zod", names: ["z"] }],
374
+ metadata: {
375
+ consume: createZodConsumeCallback(entity.name),
376
+ },
377
+ });
378
+ if (resolvedConfig.exportTypes) {
379
+ rendered.push({
380
+ name: entity.name,
381
+ capability: `schema:zod:${entity.name}:type`,
382
+ node: typeDecl,
383
+ exports: "named",
384
+ externalImports: [{ from: "zod", names: ["z"] }],
385
+ });
386
+ }
387
+ }
388
+ }
389
+ // Render the schema builder (virtual symbol - no node, just metadata)
390
+ // The builder is used by HTTP plugins to generate inline param schemas
391
+ rendered.push({
392
+ name: "zodSchemaBuilder",
393
+ capability: "schema:zod:builder",
394
+ node: null, // Virtual symbol - no emitted code
395
+ exports: false, // Not exported
396
+ metadata: {
397
+ builder: zodSchemaBuilder,
398
+ },
396
399
  });
397
- return undefined;
398
- },
399
- });
400
+ return rendered;
401
+ }),
402
+ };
400
403
  }
401
404
  //# sourceMappingURL=zod.js.map