@danielfgray/pg-sourcerer 0.2.2 → 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,101 +1,76 @@
1
1
  /**
2
- * HTTP oRPC Plugin - Generate oRPC routers from query plugins
2
+ * HTTP oRPC Plugin - Generates oRPC routers from query symbols
3
3
  *
4
- * Consumes method symbols from sql-queries or kysely-queries via the symbol registry
5
- * and generates type-safe oRPC procedures using @orpc/server.
4
+ * Consumes "queries" and "schema" capabilities (provider-agnostic).
5
+ * Works with any queries provider (kysely, drizzle, effect-sql, etc.)
6
+ * and any schema provider (zod, arktype, effect, etc.).
6
7
  *
7
- * Uses oRPC's `type()` utility for simple params (type-only, no runtime validation)
8
- * and imports body schemas from whatever plugin provides the "schemas" capability
9
- * (zod, arktype, valibot, etc.) for runtime validation.
8
+ * Uses the SymbolRegistry to resolve query functions and optionally
9
+ * schema symbols for request validation.
10
+ *
11
+ * Imports are resolved via the cross-reference system:
12
+ * - Calls registry.import(queryCapability).ref() during render
13
+ * - Emit phase generates imports from the recorded references
10
14
  */
11
- import { Schema as S } from "effect";
12
- import { definePlugin } from "../services/plugin.js";
13
- import { conjure, cast } from "../lib/conjure.js";
14
- import { inflect } from "../services/inflection.js";
15
- const { b } = conjure;
15
+ import { Effect, Schema as S } from "effect";
16
+ import { IR } from "../services/ir.js";
17
+ import { Inflection } from "../services/inflection.js";
18
+ import { SymbolRegistry } from "../runtime/registry.js";
19
+ import { isTableEntity } from "../ir/semantic-ir.js";
20
+ import { conjure, cast } from "../conjure/index.js";
21
+ import { normalizeFileNaming } from "../runtime/file-assignment.js";
22
+ const b = conjure.b;
23
+ const stmt = conjure.stmt;
24
+ const PLUGIN_NAME = "orpc-http";
16
25
  // ============================================================================
17
- // Configuration Schema
26
+ // Configuration
18
27
  // ============================================================================
28
+ const DEFAULT_OUTPUT_DIR = "";
29
+ const DEFAULT_ROUTES_FILE = "orpc.ts";
30
+ const DEFAULT_APP_FILE = "orpc.ts";
31
+ /**
32
+ * Schema-validated portion of the config (simple types only).
33
+ */
19
34
  const HttpOrpcConfigSchema = S.Struct({
20
- /** Output directory for generated router files. Default: "orpc" */
21
- outputDir: S.optionalWith(S.String, { default: () => "orpc" }),
22
- /**
23
- * Header content to prepend to each generated file.
24
- * MUST import `os` from @orpc/server.
25
- *
26
- * @example
27
- * ```typescript
28
- * header: `import { os } from "@orpc/server";`
29
- * ```
30
- */
31
- header: S.String,
35
+ outputDir: S.optionalWith(S.String, { default: () => DEFAULT_OUTPUT_DIR }),
32
36
  /** Name of the aggregated router export. Default: "appRouter" */
33
37
  aggregatorName: S.optionalWith(S.String, { default: () => "appRouter" }),
34
38
  });
35
39
  // ============================================================================
36
- // Type String Builders (for oRPC type<>() utility)
40
+ // Procedure Builders
37
41
  // ============================================================================
38
42
  /**
39
- * Convert a QueryMethodParam to a TypeScript type string for use in type<>().
43
+ * Build Zod type expression for a param.
40
44
  */
41
- const paramToTypeString = (param) => {
42
- const baseType = param.type.replace(/\[\]$/, "").replace(/\?$/, "").toLowerCase();
43
- let tsType;
45
+ function buildZodParamType(param) {
46
+ const baseType = param.type.toLowerCase();
47
+ let zodCall;
44
48
  switch (baseType) {
45
49
  case "number":
46
- case "int":
47
- case "integer":
48
- case "float":
49
- case "double":
50
- tsType = "number";
50
+ zodCall = b.callExpression(b.memberExpression(b.memberExpression(b.identifier("z"), b.identifier("coerce")), b.identifier("number")), []);
51
51
  break;
52
52
  case "boolean":
53
- case "bool":
54
- tsType = "boolean";
53
+ zodCall = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("boolean")), []);
55
54
  break;
56
55
  case "date":
57
- tsType = "Date";
56
+ zodCall = b.callExpression(b.memberExpression(b.memberExpression(b.identifier("z"), b.identifier("coerce")), b.identifier("date")), []);
58
57
  break;
59
58
  case "string":
60
59
  default:
61
- tsType = "string";
60
+ zodCall = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("string")), []);
62
61
  break;
63
62
  }
64
- // Handle arrays
65
- if (param.type.endsWith("[]")) {
66
- tsType = `${tsType}[]`;
67
- }
68
- // Handle optionality
69
63
  if (!param.required) {
70
- tsType = `${tsType} | undefined`;
64
+ zodCall = b.callExpression(b.memberExpression(cast.toExpr(zodCall), b.identifier("optional")), []);
71
65
  }
72
- return tsType;
73
- };
74
- /**
75
- * Build a TypeScript object type literal string for type<>().
76
- * Returns something like "{ id: number; name?: string }"
77
- */
78
- const buildTypeObjectString = (params) => {
79
- if (params.length === 0)
80
- return "{}";
81
- const fields = params.map((param) => {
82
- const typeStr = paramToTypeString(param);
83
- const optional = !param.required ? "?" : "";
84
- return `${param.name}${optional}: ${typeStr.replace(" | undefined", "")}`;
85
- });
86
- return `{ ${fields.join("; ")} }`;
87
- };
88
- // ============================================================================
89
- // Procedure Builders
90
- // ============================================================================
66
+ return zodCall;
67
+ }
91
68
  /**
92
- * Build the handler function body for a procedure.
93
- * oRPC handlers receive { input, context } and return data directly.
69
+ * Build the handler function body for an oRPC procedure.
70
+ * oRPC handlers receive { input } and return data directly.
94
71
  */
95
- const buildProcedureBody = (queryFnName, method) => {
72
+ function buildProcedureBody(method) {
96
73
  const callSig = method.callSignature ?? { style: "named" };
97
- const statements = [];
98
- // Build the function call arguments based on callSignature
99
74
  const args = [];
100
75
  if (callSig.style === "positional") {
101
76
  // Positional: fn(a, b, c)
@@ -104,7 +79,7 @@ const buildProcedureBody = (queryFnName, method) => {
104
79
  }
105
80
  }
106
81
  else {
107
- // Named: fn({ a, b, c }) or fn(input) for body
82
+ // Named style
108
83
  const bodyParam = method.params.find((p) => p.source === "body");
109
84
  if (bodyParam && callSig.bodyStyle === "spread") {
110
85
  // Body fields spread directly: fn(input)
@@ -112,29 +87,19 @@ const buildProcedureBody = (queryFnName, method) => {
112
87
  }
113
88
  else if (bodyParam && callSig.bodyStyle === "property") {
114
89
  // Body wrapped in property: fn({ id, data })
115
- // Collect non-body params that need to be extracted from input
116
90
  const nonBodyParams = method.params.filter((p) => p.source === "pk" || p.source === "fk" || p.source === "lookup" || p.source === "pagination");
117
91
  if (nonBodyParams.length > 0) {
118
- // Generate: const { id, ...data } = input;
119
- const destructureProps = nonBodyParams.map((p) => b.property.from({ kind: "init", key: b.identifier(p.name), value: b.identifier(p.name), shorthand: true }));
120
- const restId = b.identifier(bodyParam.name);
121
- const restElem = b.restElement(restId);
122
- const pattern = b.objectPattern([...destructureProps, restElem]);
123
- const destructureDecl = b.variableDeclaration("const", [
124
- b.variableDeclarator(pattern, b.identifier("input")),
125
- ]);
126
- statements.push(destructureDecl);
127
- // Build: { id, data } using the destructured variables
92
+ // Build object with non-body params + body property
128
93
  let objBuilder = conjure.obj();
129
94
  for (const param of nonBodyParams) {
130
- objBuilder = objBuilder.shorthand(param.name);
95
+ objBuilder = objBuilder.prop(param.name, b.memberExpression(b.identifier("input"), b.identifier(param.name)));
131
96
  }
132
- objBuilder = objBuilder.shorthand(bodyParam.name);
97
+ objBuilder = objBuilder.prop(bodyParam.name, b.memberExpression(b.identifier("input"), b.identifier(bodyParam.name)));
133
98
  args.push(objBuilder.build());
134
99
  }
135
100
  else {
136
- // No non-body params, just wrap input: fn({ data: input })
137
- args.push(conjure.obj().prop(bodyParam.name, b.identifier("input")).build());
101
+ // No non-body params, just pass input
102
+ args.push(b.identifier("input"));
138
103
  }
139
104
  }
140
105
  else if (method.params.length > 0) {
@@ -142,229 +107,352 @@ const buildProcedureBody = (queryFnName, method) => {
142
107
  args.push(b.identifier("input"));
143
108
  }
144
109
  }
145
- // Build: return await queryFn(args)
146
- const queryCall = b.callExpression(b.identifier(queryFnName), args.map(cast.toExpr));
147
- const awaitExpr = b.awaitExpression(queryCall);
110
+ // Build: queryFn(args)
111
+ const queryCall = b.callExpression(b.identifier(method.name), args.map(cast.toExpr));
112
+ // Add the appropriate .execute*() method based on query kind
113
+ const executeMethod = method.kind === "read" || (method.kind === "lookup" && method.isUniqueLookup)
114
+ ? "executeTakeFirst"
115
+ : method.kind === "create" || method.kind === "update"
116
+ ? "executeTakeFirstOrThrow"
117
+ : "execute";
118
+ const queryWithExecute = b.callExpression(b.memberExpression(queryCall, b.identifier(executeMethod)), []);
119
+ const awaitExpr = b.awaitExpression(queryWithExecute);
120
+ // For delete, return success object
148
121
  if (method.kind === "delete") {
149
- statements.push(b.expressionStatement(awaitExpr));
150
- statements.push(b.returnStatement(conjure.obj().prop("success", b.booleanLiteral(true)).build()));
151
- return statements;
122
+ return [
123
+ b.expressionStatement(awaitExpr),
124
+ b.returnStatement(conjure.obj().prop("success", b.booleanLiteral(true)).build()),
125
+ ];
152
126
  }
153
- statements.push(b.returnStatement(awaitExpr));
154
- return statements;
155
- };
127
+ return [b.returnStatement(awaitExpr)];
128
+ }
156
129
  /**
157
- * Determine if a method needs body validation and which schema to use.
130
+ * Get the body schema name for a method if it needs validation.
158
131
  */
159
- const getBodySchemaImport = (method, entityName) => {
132
+ function getBodySchemaName(method, entityName) {
160
133
  if (method.kind === "create") {
161
- return { entity: entityName, shape: "insert", schemaName: `${entityName}Insert` };
134
+ return `${entityName}Insert`;
162
135
  }
163
136
  if (method.kind === "update") {
164
- return { entity: entityName, shape: "update", schemaName: `${entityName}Update` };
137
+ return `${entityName}Update`;
165
138
  }
166
139
  return null;
167
- };
140
+ }
168
141
  /**
169
- * Build Zod type expression for a param.
142
+ * Build input schema expression for a procedure.
143
+ * Returns the schema expression and whether we need z import.
170
144
  */
171
- const buildZodParamType = (param) => {
172
- const baseType = param.type.toLowerCase();
173
- let zodCall;
174
- switch (baseType) {
175
- case "number":
176
- zodCall = b.callExpression(b.memberExpression(b.memberExpression(b.identifier("z"), b.identifier("coerce")), b.identifier("number")), []);
177
- break;
178
- case "boolean":
179
- zodCall = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("boolean")), []);
180
- break;
181
- case "date":
182
- zodCall = b.callExpression(b.memberExpression(b.memberExpression(b.identifier("z"), b.identifier("coerce")), b.identifier("date")), []);
183
- break;
184
- case "string":
185
- default:
186
- zodCall = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("string")), []);
187
- break;
145
+ function buildInputSchema(method, entityName) {
146
+ const bodySchemaName = getBodySchemaName(method, entityName);
147
+ const nonBodyParams = method.params.filter((p) => p.source !== "body");
148
+ const callSig = method.callSignature ?? { style: "named" };
149
+ // For update with bodyStyle: "property", merge PK params with body schema
150
+ if (bodySchemaName && nonBodyParams.length > 0 && callSig.bodyStyle === "property") {
151
+ let objBuilder = conjure.obj();
152
+ for (const p of nonBodyParams) {
153
+ objBuilder = objBuilder.prop(p.name, buildZodParamType(p));
154
+ }
155
+ const zodObject = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("object")), [cast.toExpr(objBuilder.build())]);
156
+ const mergedSchema = b.callExpression(b.memberExpression(zodObject, b.identifier("merge")), [b.identifier(bodySchemaName)]);
157
+ return {
158
+ inputExpr: mergedSchema,
159
+ bodySchemaName,
160
+ needsZodImport: true,
161
+ needsTypeImport: false,
162
+ };
188
163
  }
189
- if (!param.required) {
190
- zodCall = b.callExpression(b.memberExpression(cast.toExpr(zodCall), b.identifier("optional")), []);
164
+ // Body params only use imported entity schemas
165
+ if (bodySchemaName) {
166
+ return {
167
+ inputExpr: b.identifier(bodySchemaName),
168
+ bodySchemaName,
169
+ needsZodImport: false,
170
+ needsTypeImport: false,
171
+ };
191
172
  }
192
- return zodCall;
193
- };
173
+ // Non-body params: build inline z.object
174
+ if (nonBodyParams.length === 0) {
175
+ return { inputExpr: null, bodySchemaName: null, needsZodImport: false, needsTypeImport: false };
176
+ }
177
+ let objBuilder = conjure.obj();
178
+ for (const param of nonBodyParams) {
179
+ const zodType = buildZodParamType(param);
180
+ objBuilder = objBuilder.prop(param.name, zodType);
181
+ }
182
+ return {
183
+ inputExpr: b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("object")), [cast.toExpr(objBuilder.build())]),
184
+ bodySchemaName: null,
185
+ needsZodImport: true,
186
+ needsTypeImport: false,
187
+ };
188
+ }
194
189
  /**
195
- * Build a single oRPC procedure.
196
- * Returns: os.input(type<InputType>()).handler(async ({ input }) => { ... })
197
- * Or for body schemas: os.input(BodySchema).handler(...)
190
+ * Build a single oRPC procedure expression.
198
191
  */
199
- const buildProcedure = (method, entityName, queryFnName) => {
200
- const bodySchema = getBodySchemaImport(method, entityName);
201
- let needsType = false;
202
- let needsZMerge = false;
192
+ function buildProcedure(method, entityName) {
193
+ // Start with os
203
194
  let chainExpr = b.identifier("os");
204
- if (method.params.length > 0) {
205
- let inputSchema;
206
- // Check for non-body params (PK, FK, lookup, pagination)
207
- const nonBodyParams = method.params.filter(p => p.source !== "body");
208
- const callSig = method.callSignature ?? { style: "named" };
209
- if (bodySchema && nonBodyParams.length > 0 && callSig.bodyStyle === "property") {
210
- // For update-style operations with bodyStyle: "property", we need to merge
211
- // the PK/FK params with the body schema: z.object({ id: z.number() }).merge(PostUpdate)
212
- needsZMerge = true;
213
- // Build: z.object({ id: z.coerce.number() })
214
- let objBuilder = conjure.obj();
215
- for (const p of nonBodyParams) {
216
- objBuilder = objBuilder.prop(p.name, buildZodParamType(p));
217
- }
218
- const zodObject = b.callExpression(b.memberExpression(b.identifier("z"), b.identifier("object")), [cast.toExpr(objBuilder.build())]);
219
- // Build: z.object({ id: ... }).merge(PostUpdate)
220
- inputSchema = b.callExpression(b.memberExpression(zodObject, b.identifier("merge")), [b.identifier(bodySchema.schemaName)]);
221
- }
222
- else if (bodySchema) {
223
- inputSchema = b.identifier(bodySchema.schemaName);
224
- }
225
- else {
226
- needsType = true;
227
- const typeStr = buildTypeObjectString(method.params);
228
- inputSchema = b.identifier(`type<${typeStr}>()`);
229
- }
230
- chainExpr = b.callExpression(b.memberExpression(cast.toExpr(chainExpr), b.identifier("input")), [cast.toExpr(inputSchema)]);
195
+ // Build input schema
196
+ const { inputExpr, bodySchemaName, needsZodImport, needsTypeImport } = buildInputSchema(method, entityName);
197
+ // Add .input(schema) if there are params
198
+ if (inputExpr) {
199
+ chainExpr = b.callExpression(b.memberExpression(cast.toExpr(chainExpr), b.identifier("input")), [cast.toExpr(inputExpr)]);
231
200
  }
201
+ // Build the handler: async ({ input }) => { ... }
232
202
  const handlerParams = [];
233
203
  if (method.params.length > 0) {
234
204
  const inputProp = b.objectProperty(b.identifier("input"), b.identifier("input"));
235
205
  inputProp.shorthand = true;
236
206
  handlerParams.push(inputProp);
237
207
  }
238
- const handlerBody = buildProcedureBody(queryFnName, method);
208
+ const handlerBody = buildProcedureBody(method);
239
209
  const handler = b.arrowFunctionExpression([b.objectPattern(handlerParams)], b.blockStatement(handlerBody.map(cast.toStmt)));
240
210
  handler.async = true;
211
+ // Add .handler()
241
212
  chainExpr = b.callExpression(b.memberExpression(cast.toExpr(chainExpr), b.identifier("handler")), [handler]);
242
- return { procedureExpr: chainExpr, bodySchema, needsType, needsZMerge };
243
- };
213
+ return { procedureExpr: chainExpr, bodySchemaName, needsZodImport, needsTypeImport };
214
+ }
215
+ /**
216
+ * Get the capability suffix for a query method.
217
+ */
218
+ function getMethodCapabilitySuffix(method, inflection) {
219
+ switch (method.kind) {
220
+ case "read":
221
+ return "findById";
222
+ case "list":
223
+ return "list";
224
+ case "create":
225
+ return "create";
226
+ case "update":
227
+ return "update";
228
+ case "delete":
229
+ return "delete";
230
+ case "lookup":
231
+ if (method.lookupField) {
232
+ const pascalField = inflection.pascalCase(method.lookupField);
233
+ return `findBy${pascalField}`;
234
+ }
235
+ return "lookup";
236
+ case "function":
237
+ return method.name;
238
+ }
239
+ }
240
+ /**
241
+ * Generate oRPC router for an entity.
242
+ */
243
+ function generateOrpcRouter(entityName, queries, config, registry, inflection) {
244
+ const routerName = inflection.variableName(entityName, "Router");
245
+ let needsZodImport = false;
246
+ let needsTypeImport = false;
247
+ const bodySchemaNames = [];
248
+ // Build router object
249
+ let routerObjBuilder = conjure.obj();
250
+ for (const method of queries.methods) {
251
+ // Record cross-reference for this query method
252
+ const methodCapability = `queries:${entityName}:${getMethodCapabilitySuffix(method, inflection)}`;
253
+ if (registry.has(methodCapability)) {
254
+ registry.import(methodCapability).ref();
255
+ }
256
+ const { procedureExpr, bodySchemaName, needsZodImport: methodNeedsZod, needsTypeImport: methodNeedsType } = buildProcedure(method, entityName);
257
+ if (bodySchemaName && !bodySchemaNames.includes(bodySchemaName)) {
258
+ bodySchemaNames.push(bodySchemaName);
259
+ // Import schema via cross-reference system
260
+ const schemaCapability = `schema:${bodySchemaName}`;
261
+ if (registry.has(schemaCapability)) {
262
+ registry.import(schemaCapability).ref();
263
+ }
264
+ }
265
+ if (methodNeedsZod)
266
+ needsZodImport = true;
267
+ if (methodNeedsType)
268
+ needsTypeImport = true;
269
+ routerObjBuilder = routerObjBuilder.prop(method.name, procedureExpr);
270
+ }
271
+ // Build: export const userRouter = { ... }
272
+ const variableDeclarator = b.variableDeclarator(b.identifier(routerName), cast.toExpr(routerObjBuilder.build()));
273
+ const variableDeclaration = b.variableDeclaration("const", [variableDeclarator]);
274
+ const externalImports = [];
275
+ if (needsZodImport) {
276
+ externalImports.push({ from: "zod", names: ["z"] });
277
+ }
278
+ if (needsTypeImport) {
279
+ externalImports.push({ from: "@orpc/server", names: ["type"] });
280
+ }
281
+ return {
282
+ statements: [variableDeclaration],
283
+ externalImports,
284
+ };
285
+ }
286
+ /**
287
+ * Generate aggregator router that combines all entity routers.
288
+ */
289
+ function generateAggregator(entities, config, registry, inflection) {
290
+ const entityEntries = Array.from(entities.entries());
291
+ if (entityEntries.length === 0) {
292
+ return { statements: [], externalImports: [] };
293
+ }
294
+ // Build: { user: userRouter, post: postRouter, ... }
295
+ let routerObjBuilder = conjure.obj();
296
+ for (const [entityName] of entityEntries) {
297
+ const routerName = inflection.variableName(entityName, "Router");
298
+ const key = inflection.variableName(entityName, "");
299
+ routerObjBuilder = routerObjBuilder.prop(key, b.identifier(routerName));
300
+ // Record cross-reference to the entity's router capability
301
+ const routeCapability = `http-routes:orpc:${entityName}`;
302
+ if (registry.has(routeCapability)) {
303
+ registry.import(routeCapability).ref();
304
+ }
305
+ }
306
+ const variableDeclarator = b.variableDeclarator(b.identifier(config.aggregatorName), cast.toExpr(routerObjBuilder.build()));
307
+ const variableDeclaration = b.variableDeclaration("const", [variableDeclarator]);
308
+ // Also export the type: export type AppRouter = typeof appRouter
309
+ const typeExport = b.exportNamedDeclaration(b.tsTypeAliasDeclaration(b.identifier("AppRouter"), b.tsTypeQuery(b.identifier(config.aggregatorName))));
310
+ return {
311
+ statements: [variableDeclaration, typeExport],
312
+ externalImports: [],
313
+ };
314
+ }
244
315
  // ============================================================================
245
316
  // Plugin Definition
246
317
  // ============================================================================
247
318
  /**
248
- * Create an http-orpc provider that generates oRPC routers.
319
+ * Create an http-orpc plugin that generates oRPC routers.
249
320
  *
250
321
  * @example
251
322
  * ```typescript
252
- * import { httpOrpc } from "pg-sourcerer"
323
+ * import { orpc } from "pg-sourcerer"
253
324
  *
254
325
  * export default defineConfig({
255
326
  * plugins: [
256
327
  * zod(),
257
- * sqlQueries(),
258
- * httpOrpc({
259
- * header: `import { os } from "@orpc/server";`,
328
+ * kyselyQueries(),
329
+ * orpc({
330
+ * aggregatorName: "appRouter",
260
331
  * }),
261
332
  * ],
262
333
  * })
263
334
  * ```
264
335
  */
265
- export function httpOrpc(config) {
266
- const parsed = S.decodeUnknownSync(HttpOrpcConfigSchema)(config);
267
- return definePlugin({
268
- name: "http-orpc",
269
- kind: "http-routes",
270
- singleton: true,
271
- canProvide: () => true,
272
- requires: () => [
273
- { kind: "queries", params: {} },
274
- { kind: "schemas", params: {} },
336
+ export function orpc(config) {
337
+ const schemaConfig = S.decodeSync(HttpOrpcConfigSchema)(config ?? {});
338
+ const resolvedConfig = {
339
+ outputDir: schemaConfig.outputDir,
340
+ aggregatorName: schemaConfig.aggregatorName,
341
+ routesFile: normalizeFileNaming(config?.routesFile, DEFAULT_ROUTES_FILE),
342
+ appFile: normalizeFileNaming(config?.appFile, DEFAULT_APP_FILE),
343
+ orpcImport: config?.orpcImport,
344
+ };
345
+ return {
346
+ name: PLUGIN_NAME,
347
+ provides: [],
348
+ fileDefaults: [
349
+ // Entity routers use routesFile config
350
+ {
351
+ pattern: "http-routes:orpc:",
352
+ outputDir: resolvedConfig.outputDir,
353
+ fileNaming: resolvedConfig.routesFile,
354
+ },
355
+ // App aggregator uses appFile config (more specific pattern wins)
356
+ {
357
+ pattern: "http-routes:orpc:app",
358
+ outputDir: resolvedConfig.outputDir,
359
+ fileNaming: resolvedConfig.appFile,
360
+ },
275
361
  ],
276
- provide: (_params, _deps, ctx) => {
277
- const { outputDir, header, aggregatorName } = parsed;
278
- // Get all entities with registered query methods
279
- const entityNames = ctx.symbols.getEntitiesWithMethods();
280
- if (entityNames.length === 0) {
281
- return;
282
- }
283
- // Track generated routers for aggregator
284
- const generatedRouters = [];
285
- // Generate router for each entity
286
- for (const entityName of entityNames) {
287
- const entityMethods = ctx.symbols.getEntityMethods(entityName);
288
- if (!entityMethods || entityMethods.methods.length === 0)
362
+ declare: Effect.gen(function* () {
363
+ const ir = yield* IR;
364
+ const inflection = yield* Inflection;
365
+ const declarations = [];
366
+ // Declare routers for all table entities that might have queries
367
+ for (const entity of ir.entities.values()) {
368
+ if (!isTableEntity(entity))
289
369
  continue;
290
- const filePath = `${outputDir}/${inflect.uncapitalize(entityName)}.ts`;
291
- const routerName = `${inflect.uncapitalize(entityName)}Router`;
292
- const file = ctx.file(filePath);
293
- // Header provides os import
294
- file.header(header);
295
- const queriesImportPath = `../${entityMethods.importPath.replace(/\.ts$/, ".js")}`;
296
- file.import({
297
- kind: "relative",
298
- namespace: "Queries",
299
- from: queriesImportPath,
300
- });
301
- const bodySchemaImports = [];
302
- let fileNeedsType = false;
303
- let fileNeedsZod = false;
304
- let routerObjBuilder = conjure.obj();
305
- for (const method of entityMethods.methods) {
306
- const queryFnName = `Queries.${method.name}`;
307
- const { procedureExpr, bodySchema, needsType, needsZMerge } = buildProcedure(method, entityName, queryFnName);
308
- if (bodySchema)
309
- bodySchemaImports.push(bodySchema);
310
- if (needsType)
311
- fileNeedsType = true;
312
- if (needsZMerge)
313
- fileNeedsZod = true;
314
- routerObjBuilder = routerObjBuilder.prop(method.name, procedureExpr);
315
- }
316
- // Import type utility if needed
317
- if (fileNeedsType) {
318
- file.import({ kind: "package", names: ["type"], from: "@orpc/server" });
319
- }
320
- // Import zod if needed for merged schemas
321
- if (fileNeedsZod) {
322
- file.import({ kind: "package", names: ["z"], from: "zod" });
323
- }
324
- // Import body schemas
325
- for (const schemaImport of bodySchemaImports) {
326
- file.import({
327
- kind: "symbol",
328
- ref: {
329
- capability: "schemas",
330
- entity: schemaImport.entity,
331
- shape: schemaImport.shape,
332
- },
370
+ if (entity.tags.omit === true)
371
+ continue;
372
+ const hasAnyPermissions = entity.permissions.canSelect ||
373
+ entity.permissions.canInsert ||
374
+ entity.permissions.canUpdate ||
375
+ entity.permissions.canDelete;
376
+ if (hasAnyPermissions) {
377
+ declarations.push({
378
+ name: inflection.variableName(entity.name, "Router"),
379
+ capability: `http-routes:orpc:${entity.name}`,
380
+ baseEntityName: entity.name,
333
381
  });
334
382
  }
335
- const routerExport = conjure.export.const(routerName, routerObjBuilder.build());
336
- // Emit router export
337
- file.ast(conjure.program(routerExport)).emit();
338
- generatedRouters.push({
339
- fileName: `${inflect.uncapitalize(entityName)}.js`,
340
- routerName,
383
+ }
384
+ // Also declare the aggregator
385
+ declarations.push({
386
+ name: resolvedConfig.aggregatorName,
387
+ capability: "http-routes:orpc:app",
388
+ });
389
+ return declarations;
390
+ }),
391
+ render: Effect.gen(function* () {
392
+ const ir = yield* IR;
393
+ const registry = yield* SymbolRegistry;
394
+ const inflection = yield* Inflection;
395
+ const rendered = [];
396
+ // Query the registry for all entity query capabilities
397
+ const entityQueries = new Map();
398
+ const queryCapabilities = registry.query("queries:");
399
+ for (const decl of queryCapabilities) {
400
+ // Only look at aggregate capabilities (queries:impl:EntityName, not queries:impl:EntityName:method)
401
+ const parts = decl.capability.split(":");
402
+ if (parts.length !== 3)
403
+ continue;
404
+ const entityName = parts[2];
405
+ const metadata = registry.getMetadata(decl.capability);
406
+ if (metadata && typeof metadata === "object" && "methods" in metadata) {
407
+ entityQueries.set(entityName, metadata);
408
+ }
409
+ }
410
+ // User module imports for oRPC builder (if configured)
411
+ const orpcUserImports = resolvedConfig.orpcImport
412
+ ? [resolvedConfig.orpcImport]
413
+ : undefined;
414
+ for (const [entityName, queries] of entityQueries) {
415
+ const entity = ir.entities.get(entityName);
416
+ if (!entity || !isTableEntity(entity))
417
+ continue;
418
+ const capability = `http-routes:orpc:${entityName}`;
419
+ // Scope cross-references to this specific capability
420
+ const { statements, externalImports } = registry.forSymbol(capability, () => generateOrpcRouter(entityName, queries, resolvedConfig, registry, inflection));
421
+ rendered.push({
422
+ name: inflection.variableName(entityName, "Router"),
423
+ capability,
424
+ node: statements[0],
425
+ exports: "named",
426
+ externalImports,
427
+ userImports: orpcUserImports,
341
428
  });
342
429
  }
343
- // Generate aggregator index.ts
344
- if (generatedRouters.length > 0) {
345
- const indexPath = `${outputDir}/index.ts`;
346
- const indexFile = ctx.file(indexPath);
347
- // Header provides os import
348
- indexFile.header(header);
349
- for (const route of generatedRouters) {
350
- indexFile.import({
351
- kind: "relative",
352
- names: [route.routerName],
353
- from: `./${route.fileName}`,
430
+ if (entityQueries.size > 0) {
431
+ const appCapability = "http-routes:orpc:app";
432
+ // Scope cross-references to the app capability
433
+ const { statements, externalImports } = registry.forSymbol(appCapability, () => generateAggregator(entityQueries, resolvedConfig, registry, inflection));
434
+ // The aggregator has multiple statements (const + type export)
435
+ rendered.push({
436
+ name: resolvedConfig.aggregatorName,
437
+ capability: appCapability,
438
+ node: statements[0], // The const declaration
439
+ exports: "named",
440
+ externalImports,
441
+ userImports: orpcUserImports,
442
+ });
443
+ // Add the type export as a separate rendered symbol
444
+ if (statements[1]) {
445
+ rendered.push({
446
+ name: "AppRouter",
447
+ capability: "http-routes:orpc:app:type",
448
+ node: statements[1],
449
+ exports: false, // Already has export in the node
450
+ // No userImports needed for type export
354
451
  });
355
452
  }
356
- // Build: export const appRouter = { user: userRouter, ... }
357
- let routerObjBuilder = conjure.obj();
358
- for (const route of generatedRouters) {
359
- const key = route.routerName.replace(/Router$/, "");
360
- routerObjBuilder = routerObjBuilder.prop(key, b.identifier(route.routerName));
361
- }
362
- const routerExport = conjure.export.const(aggregatorName, routerObjBuilder.build());
363
- // Also export the type
364
- const typeExport = b.exportNamedDeclaration(b.tsTypeAliasDeclaration(b.identifier("AppRouter"), b.tsTypeQuery(b.identifier(aggregatorName))));
365
- indexFile.ast(conjure.program(routerExport, typeExport)).emit();
366
453
  }
367
- },
368
- });
454
+ return rendered;
455
+ }),
456
+ };
369
457
  }
370
458
  //# sourceMappingURL=http-orpc.js.map