@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
package/dist/index.js CHANGED
@@ -4,13 +4,23 @@
4
4
  * Main entry point
5
5
  */
6
6
  // Config
7
- export { Config, TypeHint, TypeHintMatch } from "./config.js";
7
+ export { Config, TypeHint, TypeHintMatch, } from "./config.js";
8
8
  // Config Loader Service
9
9
  export { ConfigLoaderService, ConfigLoaderLive, createConfigLoader, defineConfig, } from "./services/config-loader.js";
10
10
  // Config Service (Effect DI)
11
- export { ConfigService, ConfigFromFile, ConfigWithInit, ConfigTest, getConfigSearchPaths, } from "./services/config.js";
11
+ export {
12
+ // Service tag
13
+ ConfigService, FileConfigProvider, InMemoryConfigProvider, withFallback,
14
+ // Layer constructors
15
+ ConfigFromFile, ConfigFromMemory, ConfigTest, ConfigWithFallback,
16
+ // Utilities
17
+ getConfigSearchPaths, } from "./services/config.js";
12
18
  // Errors
13
19
  export * from "./errors.js";
20
+ // User Module References
21
+ export { userModule, isUserModuleRef } from "./user-module.js";
22
+ // User Module Parser Service
23
+ export { UserModuleParserService, UserModuleParserLive, createUserModuleParser, } from "./services/user-module-parser.js";
14
24
  // IR
15
25
  export { createIRBuilder, freezeIR,
16
26
  // Type guards
@@ -20,58 +30,37 @@ getTableEntities, getEnumEntities, getDomainEntities, getCompositeEntities, getF
20
30
  export { SmartTags, ShapeKind } from "./ir/index.js";
21
31
  // IR Extensions - Contracts between providers
22
32
  export { EntityQueriesExtension as EntityQueriesExtensionSchema, FunctionsExtension as FunctionsExtensionSchema, } from "./ir/extensions/queries.js";
33
+ // IR Extensions - Schema Builder Contract
34
+ export { SCHEMA_BUILDER_KIND, } from "./ir/extensions/schema-builder.js";
23
35
  // Services - IR
24
36
  export { IR } from "./services/ir.js";
25
- // Services - Artifact Store
26
- export { ArtifactStore, createArtifactStore, ArtifactStoreLive } from "./services/artifact-store.js";
27
- // Services - Plugin Meta
28
- export { PluginMeta } from "./services/plugin-meta.js";
29
- // Services - Plugin Types (new plugin API)
30
- export { PluginNotFound, PluginCycle, PluginExecutionFailed, ResourceNotResolved, Plugins, PluginsLive, definePlugin, createPluginRegistry, } from "./services/plugin.js";
31
37
  // Services - Inflection
32
38
  export { Inflection, inflect, defaultInflection, defaultTransforms, createInflection, makeInflectionLayer, composeInflectionConfigs, composeInflection, InflectionLive, } from "./services/inflection.js";
33
39
  // Services - Type Hints
34
40
  export { TypeHints, createTypeHintRegistry, emptyTypeHintRegistry, TypeHintsLive, } from "./services/type-hints.js";
35
41
  // Services - PostgreSQL Type Mapping
36
42
  export { PgTypeOid, TsType, ExtensionTypeMap, defaultPgToTs, getExtensionTypeMapping, composeMappers, wrapArrayType, wrapNullable, findEnumByPgName, findCompositeByPgName, } from "./services/pg-types.js";
37
- // Services - Symbols
38
- export { Symbols, createSymbolRegistry, SymbolsLive, } from "./services/symbols.js";
39
- // Services - Service Registry (plugin-to-plugin communication)
40
- export { Services, createServiceRegistry, ServicesLive, } from "./services/service-registry.js";
41
- // Services - Emissions
42
- export { Emissions, createEmissionBuffer, EmissionsLive, } from "./services/emissions.js";
43
- // Services - Plugin Runner
44
- export { runPlugins, } from "./services/plugin-runner.js";
45
- // Services - File Writer
46
- export { FileWriterSvc, createFileWriter, FileWriterLive, } from "./services/file-writer.js";
47
- // Services - Smart Tags Parser
48
- export { parseSmartTags, } from "./services/smart-tags-parser.js";
49
- // Services - IR Builder
50
- export { IRBuilderSvc, IRBuilderLive, createIRBuilderService, } from "./services/ir-builder.js";
51
- // Testing utilities
52
- export { PluginTestLayers, createPluginTestLayer, } from "./testing.js";
53
- // Conjure - AST builder DSL
54
- export { conjure, cast, } from "./lib/conjure.js";
55
- // Hex - SQL query building primitives
56
- export { hex, buildTemplateLiteral, buildAwaitSqlTag, buildAwaitSqlString, buildQuery, buildFirstRowDecl, buildAllRowsDecl, buildReturnQuery, } from "./lib/hex.js";
57
- // Plugins (new API)
58
- export { typesPlugin, types } from "./plugins/types.js";
43
+ // Services - File Assignment
44
+ export { parseCapabilityInfo, } from "./runtime/file-assignment.js";
45
+ // Runtime Emit
46
+ export { emitFiles, } from "./runtime/emit.js";
47
+ // =============================================================================
48
+ // Plugins
49
+ // =============================================================================
50
+ export { typesPlugin } from "./plugins/types.js";
59
51
  export { zod } from "./plugins/zod.js";
60
52
  export { arktype } from "./plugins/arktype.js";
61
53
  export { valibot } from "./plugins/valibot.js";
62
- export { sqlQueries, sqlQueries as sqlQueriesPlugin } from "./plugins/sql-queries.js";
63
- // Effect plugin (unified model + repository + http)
64
- export { effect, effectPlugin } from "./plugins/effect.js";
65
- // Kysely plugin
54
+ export { effect } from "./plugins/effect/index.js";
55
+ export { express } from "./plugins/http-express.js";
56
+ export { elysia } from "./plugins/http-elysia.js";
57
+ export { hono } from "./plugins/http-hono.js";
66
58
  export { kysely } from "./plugins/kysely.js";
67
- // HTTP plugins (new API)
68
- export { httpElysia } from "./plugins/http-elysia.js";
69
- export { httpExpress } from "./plugins/http-express.js";
70
- export { httpHono } from "./plugins/http-hono.js";
71
- export { httpTrpc } from "./plugins/http-trpc.js";
72
- export { httpOrpc } from "./plugins/http-orpc.js";
73
- // Generate orchestration
74
- export { generate, runGenerate, GenerateLive, } from "./generate.js";
75
- // Database introspection
76
- export { DatabaseIntrospectionService, DatabaseIntrospectionLive, createDatabaseIntrospection, introspectDatabase, } from "./services/introspection.js";
59
+ export { sqlQueries } from "./plugins/sql-queries.js";
60
+ export { orpc } from "./plugins/http-orpc.js";
61
+ export { trpc } from "./plugins/http-trpc.js";
62
+ // =============================================================================
63
+ // Testing Utilities
64
+ // =============================================================================
65
+ export { testIR, testIRWithEntities, testIRFromFixture, testConfig, testPlugin, testPluginEmit, } from "./testing.js";
77
66
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAkB,MAAM,EAAkC,MAAM,QAAQ,CAAC;AAghBhF,eAAO,MAAM,OAAO;;qGA0DlB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAkB,MAAM,EAAkC,MAAM,QAAQ,CAAC;AA0chF,eAAO,MAAM,OAAO;;qGAoDlB,CAAC"}
package/dist/init.js CHANGED
@@ -2,92 +2,72 @@
2
2
  * pg-sourcerer init command
3
3
  *
4
4
  * Interactive config generator using @effect/cli Prompt.
5
- * Uses conjure AST builder to generate the config file.
6
5
  */
7
6
  import { Prompt } from "@effect/cli";
8
7
  import { FileSystem } from "@effect/platform";
9
8
  import { Array, Console, Effect, HashMap, HashSet, Option, pipe } from "effect";
10
9
  import postgres from "postgres";
11
- import { conjure } from "./lib/conjure.js";
12
- import recast from "recast";
13
10
  import { introspectDatabase } from "./services/introspection.js";
14
- /** Maps plugin value to import name for config generation */
11
+ import { conjure } from "./conjure/index.js";
12
+ /** Maps plugin selection values to their import names from pg-sourcerer */
15
13
  const pluginImportNames = {
16
- types: "types",
14
+ // Type generators
15
+ types: "typesPlugin",
17
16
  zod: "zod",
18
- arktype: "arktypePlugin",
17
+ arktype: "arktype",
19
18
  effect: "effect",
19
+ valibot: "valibot",
20
+ // Kysely
21
+ "kysely-types": "kysely",
22
+ "kysely-queries": "kysely",
23
+ // SQL queries
20
24
  "sql-queries": "sqlQueries",
21
- "kysely-types": "kyselyTypesPlugin",
22
- "kysely-queries": "kyselyQueriesPlugin",
23
- "http-elysia": "httpElysiaPlugin",
24
- "http-trpc": "httpTrpcPlugin",
25
- "http-orpc": "httpOrpcPlugin",
25
+ // HTTP frameworks
26
+ "http-elysia": "elysia",
27
+ "http-express": "express",
28
+ "http-hono": "hono",
29
+ "http-trpc": "trpc",
30
+ "http-orpc": "orpc",
26
31
  };
27
- /** Get plugin info by value */
28
32
  const getPluginInfo = (value) => {
29
33
  const importName = pluginImportNames[value];
30
34
  return importName ? { value, importName } : undefined;
31
35
  };
32
36
  const POSTGRES_URL_REGEX = /^postgres(ql)?:\/\//i;
33
- /**
34
- * Parse .env content into HashMap<string, string>
35
- * Handles: KEY=value, KEY="value", KEY='value', comments, empty lines
36
- */
37
- const parseDotEnv = (content) => pipe(content.split("\n"), Array.map(line => line.trim()), Array.filter(line => line.length > 0 && !line.startsWith("#")), Array.filterMap(line => {
37
+ const parseDotEnv = (content) => pipe(content.split("\n"), Array.map((line) => line.trim()), Array.filter((line) => line.length > 0 && !line.startsWith("#")), Array.filterMap((line) => {
38
38
  const eqIndex = line.indexOf("=");
39
39
  if (eqIndex === -1)
40
40
  return Option.none();
41
41
  const key = line.slice(0, eqIndex).trim();
42
42
  let value = line.slice(eqIndex + 1).trim();
43
- // Remove surrounding quotes
44
- if ((value.startsWith('"') && value.endsWith('"')) ||
45
- (value.startsWith("'") && value.endsWith("'"))) {
43
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
46
44
  value = value.slice(1, -1);
47
45
  }
48
46
  return Option.some([key, value]);
49
47
  }), HashMap.fromIterable);
50
- /**
51
- * Filter entries matching postgres URL pattern
52
- */
53
- const filterPostgresUrls = (entries, source) => pipe(entries, HashMap.filter(value => POSTGRES_URL_REGEX.test(value)), HashMap.toEntries, Array.map(([key, value]) => ({ key, value, source })));
54
- /**
55
- * Get process.env as HashMap, excluding specified keys
56
- */
48
+ const filterPostgresUrls = (entries, source) => pipe(entries, HashMap.filter((value) => POSTGRES_URL_REGEX.test(value)), HashMap.toEntries, Array.map(([key, value]) => ({ key, value, source })));
57
49
  const processEnvExcluding = (exclude) => pipe(Object.entries(process.env), Array.filter(([key, value]) => value != null && !HashSet.has(exclude, key)), Array.map(([key, value]) => [key, value]), HashMap.fromIterable);
58
- /**
59
- * Scan .env file and process.env for postgres connection strings.
60
- */
61
50
  const scanEnvForConnectionStrings = Effect.gen(function* () {
62
51
  const fs = yield* FileSystem.FileSystem;
63
52
  const envPath = `${process.cwd()}/.env`;
64
- // Try to read .env file
65
- const dotEnvMatches = yield* fs.readFileString(envPath).pipe(Effect.map(content => filterPostgresUrls(parseDotEnv(content), ".env")), Effect.catchAll(() => Effect.succeed([])));
66
- // Scan process.env, excluding keys already found in .env
67
- const dotEnvKeys = pipe(dotEnvMatches, Array.map(m => m.key), HashSet.fromIterable);
53
+ const dotEnvMatches = yield* fs.readFileString(envPath).pipe(Effect.map((content) => filterPostgresUrls(parseDotEnv(content), ".env")), Effect.catchAll(() => Effect.succeed([])));
54
+ const dotEnvKeys = pipe(dotEnvMatches, Array.map((m) => m.key), HashSet.fromIterable);
68
55
  const processEnvMatches = filterPostgresUrls(processEnvExcluding(dotEnvKeys), "process.env");
69
56
  return [...dotEnvMatches, ...processEnvMatches];
70
57
  });
71
- // ============================================================================
72
- // Prompts
73
- // ============================================================================
74
58
  const connectionStringPrompt = Prompt.text({
75
59
  message: "Database connection string",
76
- validate: value => value.trim().length === 0
60
+ validate: (value) => value.trim().length === 0
77
61
  ? Effect.fail("Connection string is required")
78
62
  : Effect.succeed(value.trim()),
79
63
  });
80
- /**
81
- * Prompt for connection string and immediately test it.
82
- * Re-prompts on failure until successful.
83
- */
84
64
  const promptAndTestConnection = (defaultValue) => Effect.gen(function* () {
85
65
  const prompt = defaultValue
86
66
  ? Prompt.text({ message: "Database connection string", default: defaultValue })
87
67
  : connectionStringPrompt;
88
68
  const connStr = yield* prompt;
89
69
  yield* Console.log("Testing connection...");
90
- const result = yield* testConnection(connStr).pipe(Effect.map(version => ({ success: true, version })), Effect.catchAll(e => Effect.succeed({ success: false, error: e.message })));
70
+ const result = yield* testConnection(connStr).pipe(Effect.map((version) => ({ success: true, version })), Effect.catchAll((e) => Effect.succeed({ success: false, error: e.message })));
91
71
  if (result.success) {
92
72
  yield* Console.log(`✓ Connected to ${result.version}\n`);
93
73
  return connStr;
@@ -95,22 +75,17 @@ const promptAndTestConnection = (defaultValue) => Effect.gen(function* () {
95
75
  else {
96
76
  yield* Console.error(`✗ ${result.error}`);
97
77
  yield* Console.log("Please try again.\n");
98
- // On retry, use the failed value as the new default so user can edit it
99
78
  return yield* promptAndTestConnection(connStr);
100
79
  }
101
80
  });
102
- /**
103
- * Get connection string - either from detected env var or manual entry.
104
- */
105
81
  const getConnectionString = Effect.gen(function* () {
106
82
  const envMatches = yield* scanEnvForConnectionStrings;
107
- // No matches → manual entry
108
83
  if (Array.isEmptyReadonlyArray(envMatches)) {
109
84
  const connectionString = yield* promptAndTestConnection();
110
85
  return { connectionString, configValue: connectionString, isEnvRef: false };
111
86
  }
112
87
  yield* Console.log(`Found ${envMatches.length} potential connection string${envMatches.length === 1 ? "" : "s"} in environment:\n`);
113
- const choices = pipe(envMatches, Array.map(m => ({
88
+ const choices = pipe(envMatches, Array.map((m) => ({
114
89
  title: `${m.key} (${m.source})`,
115
90
  value: m.key,
116
91
  })), Array.append({ title: "Enter manually", value: "__manual__" }));
@@ -122,10 +97,9 @@ const getConnectionString = Effect.gen(function* () {
122
97
  const connectionString = yield* promptAndTestConnection();
123
98
  return { connectionString, configValue: connectionString, isEnvRef: false };
124
99
  }
125
- // Find selected match and test connection
126
- const match = pipe(envMatches, Array.findFirst(m => m.key === selected), Option.getOrThrow);
100
+ const match = pipe(envMatches, Array.findFirst((m) => m.key === selected), Option.getOrThrow);
127
101
  yield* Console.log(`\nTesting ${match.key}...`);
128
- const testResult = yield* testConnection(match.value).pipe(Effect.map(version => ({ success: true, version })), Effect.catchAll(e => Effect.succeed({ success: false, error: e.message })));
102
+ const testResult = yield* testConnection(match.value).pipe(Effect.map((version) => ({ success: true, version })), Effect.catchAll((e) => Effect.succeed({ success: false, error: e.message })));
129
103
  if (testResult.success) {
130
104
  yield* Console.log(`✓ Connected to ${testResult.version}\n`);
131
105
  return {
@@ -134,25 +108,16 @@ const getConnectionString = Effect.gen(function* () {
134
108
  isEnvRef: true,
135
109
  };
136
110
  }
137
- // Failed → fall back to manual with value as default
138
111
  yield* Console.error(`✗ ${testResult.error}`);
139
112
  yield* Console.log("Connection failed. Please enter manually.\n");
140
113
  const connectionString = yield* promptAndTestConnection(match.value);
141
114
  return { connectionString, configValue: connectionString, isEnvRef: false };
142
115
  });
143
- /**
144
- * Role prompt with explanation of when to use it.
145
- *
146
- * If using Row Level Security (RLS), set this to the role your app connects as.
147
- * This ensures generated types only include columns/tables visible to that role.
148
- * Leave empty if not using RLS or connecting as a superuser.
149
- */
150
116
  const makeRolePrompt = () => Prompt.text({
151
117
  message: "PostgreSQL role (for RLS - leave empty if not using RLS)",
152
118
  default: "",
153
- }).pipe(Prompt.map(s => s.trim() || undefined));
119
+ }).pipe(Prompt.map((s) => s.trim() || undefined));
154
120
  const makeSchemasPrompt = (introspection) => {
155
- // Group classes by schema and count tables/views
156
121
  const schemaCounts = new Map();
157
122
  for (const c of introspection.classes) {
158
123
  if (c.relkind !== "r" && c.relkind !== "v" && c.relkind !== "m")
@@ -162,7 +127,6 @@ const makeSchemasPrompt = (introspection) => {
162
127
  continue;
163
128
  schemaCounts.set(schema, (schemaCounts.get(schema) ?? 0) + 1);
164
129
  }
165
- // Sort: public first, then by count desc
166
130
  const schemas = [...schemaCounts.entries()].sort((a, b) => {
167
131
  if (a[0] === "public")
168
132
  return -1;
@@ -171,16 +135,14 @@ const makeSchemasPrompt = (introspection) => {
171
135
  return b[1] - a[1];
172
136
  });
173
137
  if (schemas.length === 0) {
174
- // Fallback to text prompt if no schemas found
175
138
  return Prompt.text({
176
139
  message: "Schemas to introspect (comma-separated)",
177
140
  default: "public",
178
- }).pipe(Prompt.map(s => s
141
+ }).pipe(Prompt.map((s) => s
179
142
  .split(",")
180
- .map(x => x.trim())
181
- .filter(x => x.length > 0)));
143
+ .map((x) => x.trim())
144
+ .filter((x) => x.length > 0)));
182
145
  }
183
- // Multi-select with table counts
184
146
  return Prompt.multiSelect({
185
147
  message: "Select schemas to introspect",
186
148
  choices: schemas.map(([name, count]) => ({
@@ -192,26 +154,14 @@ const makeSchemasPrompt = (introspection) => {
192
154
  };
193
155
  const makeOutputDirPrompt = () => Prompt.text({
194
156
  message: "Output directory",
195
- default: "./generated",
157
+ default: "./src/generated",
196
158
  });
197
- /**
198
- * Plugin selection with Kysely-aware branching.
199
- *
200
- * Flow:
201
- * 1. Kysely? → yes: checkboxes for types + kysely-queries → HTTP plugins → exit
202
- * 2. No Kysely → raw types or schema-driven?
203
- * 3. If schema-driven → select schema lib (zod/arktype/effect)
204
- * 4. Query plugins (optional multiselect, currently just sql-queries)
205
- * 5. HTTP/RPC framework (optional)
206
- */
207
159
  const makePluginsPrompt = () => Effect.gen(function* () {
208
- // Step 1: Kysely gateway
209
160
  const usesKysely = yield* Prompt.confirm({
210
161
  message: "Do you use Kysely?",
211
162
  initial: false,
212
163
  });
213
164
  if (usesKysely) {
214
- // Kysely path: checkboxes, both pre-selected
215
165
  const kyselyPlugins = yield* Prompt.multiSelect({
216
166
  message: "Select Kysely plugins",
217
167
  choices: [
@@ -219,22 +169,25 @@ const makePluginsPrompt = () => Effect.gen(function* () {
219
169
  { title: "kysely-queries - Query builders", value: "kysely-queries", selected: true },
220
170
  ],
221
171
  });
222
- // Step 5: HTTP/RPC framework (only show if queries selected)
223
172
  const hasQueries = kyselyPlugins.includes("kysely-queries");
224
173
  if (hasQueries) {
225
- const httpPlugins = yield* Prompt.multiSelect({
174
+ const httpPlugin = yield* Prompt.select({
226
175
  message: "HTTP/RPC framework (optional, requires query plugin)",
227
176
  choices: [
228
- { title: "Elysia routes", value: "http-elysia", selected: false },
229
- { title: "tRPC routers", value: "http-trpc", selected: false },
230
- { title: "oRPC routers", value: "http-orpc", selected: false },
177
+ { title: "None", value: "" },
178
+ { title: "Elysia routes", value: "http-elysia" },
179
+ { title: "Express routes", value: "http-express" },
180
+ { title: "Hono routes", value: "http-hono" },
181
+ { title: "tRPC router", value: "http-trpc" },
182
+ { title: "oRPC router", value: "http-orpc" },
231
183
  ],
232
184
  });
233
- return [...kyselyPlugins, ...httpPlugins];
185
+ return httpPlugin
186
+ ? [...kyselyPlugins, httpPlugin]
187
+ : kyselyPlugins;
234
188
  }
235
189
  return kyselyPlugins;
236
190
  }
237
- // Step 2: Raw vs schema-driven
238
191
  const typeApproach = yield* Prompt.select({
239
192
  message: "How do you want your types generated?",
240
193
  choices: [
@@ -247,34 +200,36 @@ const makePluginsPrompt = () => Effect.gen(function* () {
247
200
  typePlugin = "types";
248
201
  }
249
202
  else {
250
- // Step 3: Schema library selection
251
203
  typePlugin = yield* Prompt.select({
252
204
  message: "Select schema library",
253
205
  choices: [
254
206
  { title: "Zod", value: "zod" },
255
207
  { title: "ArkType", value: "arktype" },
256
- { title: "Effect Schema (@effect/sql)", value: "effect" },
208
+ { title: "Effect Schema", value: "effect" },
257
209
  ],
258
210
  });
259
211
  }
260
- // Step 4: Query plugins (optional multiselect, future-proof)
261
212
  const queryPlugins = yield* Prompt.multiSelect({
262
213
  message: "Query generation (optional)",
263
214
  choices: [
264
215
  { title: "sql-queries - Raw SQL query functions", value: "sql-queries", selected: false },
265
216
  ],
266
217
  });
267
- // Step 5: HTTP/RPC framework (only show if queries selected)
268
218
  if (queryPlugins.length > 0) {
269
- const httpPlugins = yield* Prompt.multiSelect({
219
+ const httpPlugin = yield* Prompt.select({
270
220
  message: "HTTP/RPC framework (optional)",
271
221
  choices: [
272
- { title: "Elysia routes", value: "http-elysia", selected: false },
273
- { title: "tRPC routers", value: "http-trpc", selected: false },
274
- { title: "oRPC routers", value: "http-orpc", selected: false },
222
+ { title: "None", value: "" },
223
+ { title: "Elysia routes", value: "http-elysia" },
224
+ { title: "Express routes", value: "http-express" },
225
+ { title: "Hono routes", value: "http-hono" },
226
+ { title: "tRPC router", value: "http-trpc" },
227
+ { title: "oRPC router", value: "http-orpc" },
275
228
  ],
276
229
  });
277
- return [typePlugin, ...queryPlugins, ...httpPlugins];
230
+ return httpPlugin
231
+ ? [typePlugin, ...queryPlugins, httpPlugin]
232
+ : [typePlugin, ...queryPlugins];
278
233
  }
279
234
  return [typePlugin, ...queryPlugins];
280
235
  });
@@ -282,16 +237,12 @@ const makeFormatterPrompt = () => Prompt.text({
282
237
  message: "Formatter command (optional, leave empty to skip)",
283
238
  default: "",
284
239
  });
285
- // ============================================================================
286
- // Connection Test
287
- // ============================================================================
288
240
  const testConnection = (connectionString) => Effect.tryPromise({
289
241
  try: async () => {
290
242
  const sql = postgres(connectionString, { max: 1 });
291
243
  try {
292
244
  const result = await sql `SELECT version()`;
293
245
  const version = result[0]?.["version"];
294
- // Extract just the version number (e.g., "PostgreSQL 15.2")
295
246
  const match = version.match(/PostgreSQL [\d.]+/);
296
247
  return match?.[0] ?? "PostgreSQL";
297
248
  }
@@ -299,68 +250,60 @@ const testConnection = (connectionString) => Effect.tryPromise({
299
250
  await sql.end();
300
251
  }
301
252
  },
302
- catch: error => new Error(`Connection failed: ${error instanceof Error ? error.message : String(error)}`),
253
+ catch: (error) => new Error(`Connection failed: ${error instanceof Error ? error.message : String(error)}`),
303
254
  });
304
255
  const generateConfigContent = (answers) => {
305
- // Look up import names for selected plugins
306
- const selectedPlugins = pipe(answers.plugins, Array.filterMap(value => {
256
+ // Get unique plugin imports (dedupe e.g., kysely-types + kysely-queries -> single kysely import)
257
+ const selectedPlugins = pipe(answers.plugins, Array.filterMap((value) => {
307
258
  const info = getPluginInfo(value);
308
259
  return info ? Option.some(info) : Option.none();
309
260
  }));
310
- // Build import specifiers
311
- const coreImports = ["defineConfig"];
312
- const allImports = [...coreImports, ...selectedPlugins.map(p => p.importName)];
313
- // Build the import statement
314
- const importDecl = conjure.b.importDeclaration(allImports.map(name => conjure.b.importSpecifier(conjure.b.identifier(name))), conjure.str("@danielfgray/pg-sourcerer"));
315
- // Build the config object - handle env ref vs literal
261
+ // Deduplicate by import name (keep first occurrence)
262
+ const uniqueImports = pipe(selectedPlugins, Array.dedupeWith((a, b) => a.importName === b.importName));
263
+ const allImportNames = ["defineConfig", ...uniqueImports.map((p) => p.importName)];
264
+ // Build import declaration: import { defineConfig, plugin1, plugin2 } from "@danielfgray/pg-sourcerer"
265
+ const importDecl = conjure.import.named("@danielfgray/pg-sourcerer", ...allImportNames);
266
+ // Build config object
316
267
  let configObj = conjure.obj();
268
+ // connectionString - either env ref or literal
317
269
  if (answers.isEnvRef) {
318
- // Generate: connectionString: process.env.DATABASE_URL!
319
270
  const envKey = answers.connectionConfigValue.replace("process.env.", "");
320
- const envAccess = conjure.b.memberExpression(conjure.b.memberExpression(conjure.b.identifier("process"), conjure.b.identifier("env")), conjure.b.identifier(envKey));
321
- // Add non-null assertion (!)
322
- const envAccessWithAssertion = conjure.b.tsNonNullExpression(envAccess);
323
- configObj = configObj.prop("connectionString", envAccessWithAssertion);
271
+ // process.env.VAR!
272
+ configObj = configObj.prop("connectionString", conjure.nonNull(conjure.id("process").prop("env").prop(envKey).build()));
324
273
  }
325
274
  else {
326
275
  configObj = configObj.prop("connectionString", conjure.str(answers.connectionConfigValue));
327
276
  }
328
- // Role (only if set)
277
+ // role (optional)
329
278
  if (answers.role) {
330
279
  configObj = configObj.prop("role", conjure.str(answers.role));
331
280
  }
332
- // Schemas (only if not just ["public"])
281
+ // schemas (only if not default ["public"])
333
282
  if (answers.schemas.length !== 1 || answers.schemas[0] !== "public") {
334
- const schemasArr = conjure.arr(...answers.schemas.map(s => conjure.str(s)));
335
- configObj = configObj.prop("schemas", schemasArr.build());
283
+ configObj = configObj.prop("schemas", conjure.arr(...answers.schemas.map((s) => conjure.str(s))).build());
336
284
  }
337
- // Output directory (only if not default)
285
+ // outputDir (only if not default)
338
286
  if (answers.outputDir !== "src/generated") {
339
287
  configObj = configObj.prop("outputDir", conjure.str(answers.outputDir));
340
288
  }
341
- // Formatter (only if set)
289
+ // formatter (optional)
342
290
  if (answers.formatter.trim()) {
343
291
  configObj = configObj.prop("formatter", conjure.str(answers.formatter.trim()));
344
292
  }
345
- // Plugins array - no config needed, all plugins have sensible defaults
346
- const pluginCalls = selectedPlugins.map(p => conjure.id(p.importName).call([]).build());
347
- const pluginsArr = conjure.arr(...pluginCalls);
348
- configObj = configObj.prop("plugins", pluginsArr.build());
349
- // Build the export default statement
293
+ // plugins array: [plugin1(), plugin2(), ...]
294
+ const pluginCalls = uniqueImports.map((p) => conjure.id(p.importName).call().build());
295
+ configObj = configObj.prop("plugins", conjure.arr(...pluginCalls).build());
296
+ // Build: export default defineConfig({ ... })
350
297
  const defineConfigCall = conjure.id("defineConfig").call([configObj.build()]).build();
351
298
  const exportDefault = conjure.export.default(defineConfigCall);
352
- // Build the program and print with 2-space indentation
299
+ // Create program and print
353
300
  const program = conjure.program(importDecl, exportDefault);
354
- return recast.print(program, { tabWidth: 2 }).code + "\n";
301
+ return conjure.print(program) + "\n";
355
302
  };
356
- // ============================================================================
357
- // Main Init Effect
358
- // ============================================================================
359
303
  const CONFIG_FILENAME = "pgsourcerer.config.ts";
360
304
  export const runInit = Effect.gen(function* () {
361
305
  const fs = yield* FileSystem.FileSystem;
362
306
  const configPath = `${process.cwd()}/${CONFIG_FILENAME}`;
363
- // Check if config already exists
364
307
  const exists = yield* fs.exists(configPath);
365
308
  if (exists) {
366
309
  yield* Console.error(`\n✗ ${CONFIG_FILENAME} already exists`);
@@ -368,20 +311,14 @@ export const runInit = Effect.gen(function* () {
368
311
  return yield* Effect.fail(new Error("Config already exists"));
369
312
  }
370
313
  yield* Console.log("\n🔧 pg-sourcerer config generator\n");
371
- // Collect answers - connection string may be detected from env
372
314
  const { connectionString, configValue, isEnvRef } = yield* getConnectionString;
373
- // Introspect to discover schemas
374
315
  yield* Console.log("Introspecting database...");
375
- const introspection = yield* introspectDatabase({ connectionString }).pipe(Effect.catchAll(e => {
376
- // Log error but continue with empty introspection for fallback text prompt
377
- return Console.error(`Warning: ${e.message}`).pipe(Effect.andThen(Effect.succeed({ classes: [] })));
378
- }));
316
+ const introspection = yield* introspectDatabase({ connectionString }).pipe(Effect.catchAll((e) => Console.error(`Warning: ${e.message}`).pipe(Effect.andThen(Effect.succeed({ classes: [] })))));
379
317
  const role = yield* makeRolePrompt();
380
318
  const schemas = yield* makeSchemasPrompt(introspection);
381
319
  const outputDir = yield* makeOutputDirPrompt();
382
320
  const plugins = yield* makePluginsPrompt();
383
321
  const formatter = yield* makeFormatterPrompt();
384
- // Validate at least one plugin selected
385
322
  if (plugins.length === 0) {
386
323
  yield* Console.error("✗ At least one plugin must be selected");
387
324
  return yield* Effect.fail(new Error("No plugins selected"));
@@ -396,7 +333,6 @@ export const runInit = Effect.gen(function* () {
396
333
  plugins,
397
334
  formatter,
398
335
  };
399
- // Generate and write config
400
336
  const configContent = generateConfigContent(answers);
401
337
  yield* fs.writeFileString(configPath, configContent);
402
338
  yield* Console.log(`\n✓ Created ${CONFIG_FILENAME}`);