@pattern-stack/codegen 0.2.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 (279) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +214 -0
  3. package/dist/runtime/analytics/index.d.ts +6 -0
  4. package/dist/runtime/analytics/index.js +49 -0
  5. package/dist/runtime/analytics/index.js.map +1 -0
  6. package/dist/runtime/analytics/metrics.d.ts +75 -0
  7. package/dist/runtime/analytics/metrics.js +1 -0
  8. package/dist/runtime/analytics/metrics.js.map +1 -0
  9. package/dist/runtime/analytics/packs/crm-entity-measures.d.ts +21 -0
  10. package/dist/runtime/analytics/packs/crm-entity-measures.js +1 -0
  11. package/dist/runtime/analytics/packs/crm-entity-measures.js.map +1 -0
  12. package/dist/runtime/analytics/packs/index.d.ts +3 -0
  13. package/dist/runtime/analytics/packs/index.js +1 -0
  14. package/dist/runtime/analytics/packs/index.js.map +1 -0
  15. package/dist/runtime/analytics/packs/monetary-measures.d.ts +21 -0
  16. package/dist/runtime/analytics/packs/monetary-measures.js +1 -0
  17. package/dist/runtime/analytics/packs/monetary-measures.js.map +1 -0
  18. package/dist/runtime/analytics/specs.d.ts +49 -0
  19. package/dist/runtime/analytics/specs.js +1 -0
  20. package/dist/runtime/analytics/specs.js.map +1 -0
  21. package/dist/runtime/analytics/types.d.ts +85 -0
  22. package/dist/runtime/analytics/types.js +49 -0
  23. package/dist/runtime/analytics/types.js.map +1 -0
  24. package/dist/runtime/base-classes/activity-entity-repository.d.ts +26 -0
  25. package/dist/runtime/base-classes/activity-entity-repository.js +195 -0
  26. package/dist/runtime/base-classes/activity-entity-repository.js.map +1 -0
  27. package/dist/runtime/base-classes/activity-entity-service.d.ts +39 -0
  28. package/dist/runtime/base-classes/activity-entity-service.js +214 -0
  29. package/dist/runtime/base-classes/activity-entity-service.js.map +1 -0
  30. package/dist/runtime/base-classes/base-read-use-cases.d.ts +68 -0
  31. package/dist/runtime/base-classes/base-read-use-cases.js +32 -0
  32. package/dist/runtime/base-classes/base-read-use-cases.js.map +1 -0
  33. package/dist/runtime/base-classes/base-repository.d.ts +99 -0
  34. package/dist/runtime/base-classes/base-repository.js +160 -0
  35. package/dist/runtime/base-classes/base-repository.js.map +1 -0
  36. package/dist/runtime/base-classes/base-service.d.ts +98 -0
  37. package/dist/runtime/base-classes/base-service.js +186 -0
  38. package/dist/runtime/base-classes/base-service.js.map +1 -0
  39. package/dist/runtime/base-classes/index.d.ts +18 -0
  40. package/dist/runtime/base-classes/index.js +617 -0
  41. package/dist/runtime/base-classes/index.js.map +1 -0
  42. package/dist/runtime/base-classes/knowledge-entity-repository.d.ts +17 -0
  43. package/dist/runtime/base-classes/knowledge-entity-repository.js +166 -0
  44. package/dist/runtime/base-classes/knowledge-entity-repository.js.map +1 -0
  45. package/dist/runtime/base-classes/knowledge-entity-service.d.ts +15 -0
  46. package/dist/runtime/base-classes/knowledge-entity-service.js +192 -0
  47. package/dist/runtime/base-classes/knowledge-entity-service.js.map +1 -0
  48. package/dist/runtime/base-classes/lifecycle-events.d.ts +49 -0
  49. package/dist/runtime/base-classes/lifecycle-events.js +76 -0
  50. package/dist/runtime/base-classes/lifecycle-events.js.map +1 -0
  51. package/dist/runtime/base-classes/metadata-entity-repository.d.ts +27 -0
  52. package/dist/runtime/base-classes/metadata-entity-repository.js +212 -0
  53. package/dist/runtime/base-classes/metadata-entity-repository.js.map +1 -0
  54. package/dist/runtime/base-classes/metadata-entity-service.d.ts +39 -0
  55. package/dist/runtime/base-classes/metadata-entity-service.js +214 -0
  56. package/dist/runtime/base-classes/metadata-entity-service.js.map +1 -0
  57. package/dist/runtime/base-classes/synced-entity-repository.d.ts +32 -0
  58. package/dist/runtime/base-classes/synced-entity-repository.js +203 -0
  59. package/dist/runtime/base-classes/synced-entity-repository.js.map +1 -0
  60. package/dist/runtime/base-classes/synced-entity-service.d.ts +41 -0
  61. package/dist/runtime/base-classes/synced-entity-service.js +215 -0
  62. package/dist/runtime/base-classes/synced-entity-service.js.map +1 -0
  63. package/dist/runtime/base-classes/with-analytics.d.ts +18 -0
  64. package/dist/runtime/base-classes/with-analytics.js +11 -0
  65. package/dist/runtime/base-classes/with-analytics.js.map +1 -0
  66. package/dist/runtime/constants/tokens.d.ts +29 -0
  67. package/dist/runtime/constants/tokens.js +8 -0
  68. package/dist/runtime/constants/tokens.js.map +1 -0
  69. package/dist/runtime/subsystems/analytics/analytics-query.protocol.d.ts +30 -0
  70. package/dist/runtime/subsystems/analytics/analytics-query.protocol.js +1 -0
  71. package/dist/runtime/subsystems/analytics/analytics-query.protocol.js.map +1 -0
  72. package/dist/runtime/subsystems/analytics/analytics.module.d.ts +34 -0
  73. package/dist/runtime/subsystems/analytics/analytics.module.js +117 -0
  74. package/dist/runtime/subsystems/analytics/analytics.module.js.map +1 -0
  75. package/dist/runtime/subsystems/analytics/analytics.tokens.d.ts +24 -0
  76. package/dist/runtime/subsystems/analytics/analytics.tokens.js +10 -0
  77. package/dist/runtime/subsystems/analytics/analytics.tokens.js.map +1 -0
  78. package/dist/runtime/subsystems/analytics/cube-backend.d.ts +28 -0
  79. package/dist/runtime/subsystems/analytics/cube-backend.js +71 -0
  80. package/dist/runtime/subsystems/analytics/cube-backend.js.map +1 -0
  81. package/dist/runtime/subsystems/analytics/index.d.ts +6 -0
  82. package/dist/runtime/subsystems/analytics/index.js +122 -0
  83. package/dist/runtime/subsystems/analytics/index.js.map +1 -0
  84. package/dist/runtime/subsystems/analytics/noop-backend.d.ts +7 -0
  85. package/dist/runtime/subsystems/analytics/noop-backend.js +25 -0
  86. package/dist/runtime/subsystems/analytics/noop-backend.js.map +1 -0
  87. package/dist/runtime/subsystems/cache/cache.drizzle-backend.d.ts +43 -0
  88. package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +133 -0
  89. package/dist/runtime/subsystems/cache/cache.drizzle-backend.js.map +1 -0
  90. package/dist/runtime/subsystems/cache/cache.memory-backend.d.ts +21 -0
  91. package/dist/runtime/subsystems/cache/cache.memory-backend.js +100 -0
  92. package/dist/runtime/subsystems/cache/cache.memory-backend.js.map +1 -0
  93. package/dist/runtime/subsystems/cache/cache.module.d.ts +37 -0
  94. package/dist/runtime/subsystems/cache/cache.module.js +272 -0
  95. package/dist/runtime/subsystems/cache/cache.module.js.map +1 -0
  96. package/dist/runtime/subsystems/cache/cache.protocol.d.ts +42 -0
  97. package/dist/runtime/subsystems/cache/cache.protocol.js +1 -0
  98. package/dist/runtime/subsystems/cache/cache.protocol.js.map +1 -0
  99. package/dist/runtime/subsystems/cache/cache.schema.d.ts +64 -0
  100. package/dist/runtime/subsystems/cache/cache.schema.js +18 -0
  101. package/dist/runtime/subsystems/cache/cache.schema.js.map +1 -0
  102. package/dist/runtime/subsystems/cache/cache.tokens.d.ts +18 -0
  103. package/dist/runtime/subsystems/cache/cache.tokens.js +8 -0
  104. package/dist/runtime/subsystems/cache/cache.tokens.js.map +1 -0
  105. package/dist/runtime/subsystems/cache/index.d.ts +11 -0
  106. package/dist/runtime/subsystems/cache/index.js +277 -0
  107. package/dist/runtime/subsystems/cache/index.js.map +1 -0
  108. package/dist/runtime/subsystems/events/domain-events.schema.d.ts +187 -0
  109. package/dist/runtime/subsystems/events/domain-events.schema.js +32 -0
  110. package/dist/runtime/subsystems/events/domain-events.schema.js.map +1 -0
  111. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +38 -0
  112. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +199 -0
  113. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -0
  114. package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +18 -0
  115. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +71 -0
  116. package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -0
  117. package/dist/runtime/subsystems/events/event-bus.protocol.d.ts +52 -0
  118. package/dist/runtime/subsystems/events/event-bus.protocol.js +1 -0
  119. package/dist/runtime/subsystems/events/event-bus.protocol.js.map +1 -0
  120. package/dist/runtime/subsystems/events/event-bus.redis-backend.d.ts +95 -0
  121. package/dist/runtime/subsystems/events/event-bus.redis-backend.js +229 -0
  122. package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -0
  123. package/dist/runtime/subsystems/events/events.module.d.ts +46 -0
  124. package/dist/runtime/subsystems/events/events.module.js +531 -0
  125. package/dist/runtime/subsystems/events/events.module.js.map +1 -0
  126. package/dist/runtime/subsystems/events/events.tokens.d.ts +19 -0
  127. package/dist/runtime/subsystems/events/events.tokens.js +8 -0
  128. package/dist/runtime/subsystems/events/events.tokens.js.map +1 -0
  129. package/dist/runtime/subsystems/events/index.d.ts +12 -0
  130. package/dist/runtime/subsystems/events/index.js +536 -0
  131. package/dist/runtime/subsystems/events/index.js.map +1 -0
  132. package/dist/runtime/subsystems/index.d.ts +24 -0
  133. package/dist/runtime/subsystems/index.js +1643 -0
  134. package/dist/runtime/subsystems/index.js.map +1 -0
  135. package/dist/runtime/subsystems/jobs/index.d.ts +14 -0
  136. package/dist/runtime/subsystems/jobs/index.js +680 -0
  137. package/dist/runtime/subsystems/jobs/index.js.map +1 -0
  138. package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.d.ts +54 -0
  139. package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js +186 -0
  140. package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js.map +1 -0
  141. package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.d.ts +38 -0
  142. package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js +228 -0
  143. package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js.map +1 -0
  144. package/dist/runtime/subsystems/jobs/job-queue.memory-backend.d.ts +12 -0
  145. package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js +44 -0
  146. package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js.map +1 -0
  147. package/dist/runtime/subsystems/jobs/job-queue.protocol.d.ts +48 -0
  148. package/dist/runtime/subsystems/jobs/job-queue.protocol.js +1 -0
  149. package/dist/runtime/subsystems/jobs/job-queue.protocol.js.map +1 -0
  150. package/dist/runtime/subsystems/jobs/job-queue.redis-backend.d.ts +46 -0
  151. package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js +187 -0
  152. package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js.map +1 -0
  153. package/dist/runtime/subsystems/jobs/job-queue.schema.d.ts +237 -0
  154. package/dist/runtime/subsystems/jobs/job-queue.schema.js +44 -0
  155. package/dist/runtime/subsystems/jobs/job-queue.schema.js.map +1 -0
  156. package/dist/runtime/subsystems/jobs/jobs.module.d.ts +18 -0
  157. package/dist/runtime/subsystems/jobs/jobs.module.js +676 -0
  158. package/dist/runtime/subsystems/jobs/jobs.module.js.map +1 -0
  159. package/dist/runtime/subsystems/jobs/jobs.tokens.d.ts +13 -0
  160. package/dist/runtime/subsystems/jobs/jobs.tokens.js +8 -0
  161. package/dist/runtime/subsystems/jobs/jobs.tokens.js.map +1 -0
  162. package/dist/runtime/subsystems/storage/index.d.ts +6 -0
  163. package/dist/runtime/subsystems/storage/index.js +204 -0
  164. package/dist/runtime/subsystems/storage/index.js.map +1 -0
  165. package/dist/runtime/subsystems/storage/storage.local-backend.d.ts +18 -0
  166. package/dist/runtime/subsystems/storage/storage.local-backend.js +108 -0
  167. package/dist/runtime/subsystems/storage/storage.local-backend.js.map +1 -0
  168. package/dist/runtime/subsystems/storage/storage.memory-backend.d.ts +28 -0
  169. package/dist/runtime/subsystems/storage/storage.memory-backend.js +72 -0
  170. package/dist/runtime/subsystems/storage/storage.memory-backend.js.map +1 -0
  171. package/dist/runtime/subsystems/storage/storage.module.d.ts +40 -0
  172. package/dist/runtime/subsystems/storage/storage.module.js +201 -0
  173. package/dist/runtime/subsystems/storage/storage.module.js.map +1 -0
  174. package/dist/runtime/subsystems/storage/storage.protocol.d.ts +69 -0
  175. package/dist/runtime/subsystems/storage/storage.protocol.js +1 -0
  176. package/dist/runtime/subsystems/storage/storage.protocol.js.map +1 -0
  177. package/dist/runtime/subsystems/storage/storage.tokens.d.ts +11 -0
  178. package/dist/runtime/subsystems/storage/storage.tokens.js +6 -0
  179. package/dist/runtime/subsystems/storage/storage.tokens.js.map +1 -0
  180. package/dist/runtime/subsystems/storage/storage.utils.d.ts +9 -0
  181. package/dist/runtime/subsystems/storage/storage.utils.js +18 -0
  182. package/dist/runtime/subsystems/storage/storage.utils.js.map +1 -0
  183. package/dist/runtime/types/drizzle.d.ts +17 -0
  184. package/dist/runtime/types/drizzle.js +1 -0
  185. package/dist/runtime/types/drizzle.js.map +1 -0
  186. package/dist/src/cli/index.d.ts +1 -0
  187. package/dist/src/cli/index.js +7365 -0
  188. package/dist/src/cli/index.js.map +1 -0
  189. package/dist/src/index.d.ts +2384 -0
  190. package/dist/src/index.js +2198 -0
  191. package/dist/src/index.js.map +1 -0
  192. package/package.json +114 -0
  193. package/templates/broadcast/new/backend-interface.ejs.t +47 -0
  194. package/templates/broadcast/new/bridge-listener.ejs.t +67 -0
  195. package/templates/broadcast/new/channel.ejs.t +77 -0
  196. package/templates/broadcast/new/index.ejs.t +21 -0
  197. package/templates/broadcast/new/memory-backend.ejs.t +87 -0
  198. package/templates/broadcast/new/module.ejs.t +57 -0
  199. package/templates/broadcast/new/prompt.js +268 -0
  200. package/templates/broadcast/new/websocket-backend.ejs.t +259 -0
  201. package/templates/entity/new/backend/application/commands/create.ejs.t +55 -0
  202. package/templates/entity/new/backend/application/commands/delete.ejs.t +45 -0
  203. package/templates/entity/new/backend/application/commands/grouped-index.ejs.t +149 -0
  204. package/templates/entity/new/backend/application/commands/index.ejs.t +15 -0
  205. package/templates/entity/new/backend/application/commands/update.ejs.t +58 -0
  206. package/templates/entity/new/backend/application/queries/declarative-queries.ejs.t +36 -0
  207. package/templates/entity/new/backend/application/queries/get-by-id.ejs.t +42 -0
  208. package/templates/entity/new/backend/application/queries/grouped-index.ejs.t +81 -0
  209. package/templates/entity/new/backend/application/queries/index.ejs.t +14 -0
  210. package/templates/entity/new/backend/application/queries/list.ejs.t +36 -0
  211. package/templates/entity/new/backend/application/schemas/_inject-index.ejs.t +7 -0
  212. package/templates/entity/new/backend/application/schemas/dto.ejs.t +45 -0
  213. package/templates/entity/new/backend/database/_inject-index.ejs.t +7 -0
  214. package/templates/entity/new/backend/database/electric-migration.ejs.t +21 -0
  215. package/templates/entity/new/backend/database/repository.ejs.t +450 -0
  216. package/templates/entity/new/backend/database/schema.ejs.t +248 -0
  217. package/templates/entity/new/backend/domain/_inject-index.ejs.t +12 -0
  218. package/templates/entity/new/backend/domain/entity.ejs.t +108 -0
  219. package/templates/entity/new/backend/domain/grouped-index.ejs.t +163 -0
  220. package/templates/entity/new/backend/domain/index.ejs.t +15 -0
  221. package/templates/entity/new/backend/domain/repository-interface.ejs.t +71 -0
  222. package/templates/entity/new/backend/modules/core/_ensure-anchor-tokens.ejs.t +10 -0
  223. package/templates/entity/new/backend/modules/core/_inject-token.ejs.t +7 -0
  224. package/templates/entity/new/backend/modules/core/module.ejs.t +67 -0
  225. package/templates/entity/new/backend/modules/trpc/module.ejs.t +67 -0
  226. package/templates/entity/new/backend/presentation/controller.ejs.t +201 -0
  227. package/templates/entity/new/clean-lite-ps/controller.ejs.t +37 -0
  228. package/templates/entity/new/clean-lite-ps/dto/create.ejs.t +17 -0
  229. package/templates/entity/new/clean-lite-ps/dto/output.ejs.t +25 -0
  230. package/templates/entity/new/clean-lite-ps/dto/update.ejs.t +11 -0
  231. package/templates/entity/new/clean-lite-ps/entity.ejs.t +52 -0
  232. package/templates/entity/new/clean-lite-ps/index.ejs.t +20 -0
  233. package/templates/entity/new/clean-lite-ps/module.ejs.t +43 -0
  234. package/templates/entity/new/clean-lite-ps/prompt-extension.js +617 -0
  235. package/templates/entity/new/clean-lite-ps/repository.ejs.t +62 -0
  236. package/templates/entity/new/clean-lite-ps/service.ejs.t +34 -0
  237. package/templates/entity/new/clean-lite-ps/use-cases/declarative-queries.ejs.t +34 -0
  238. package/templates/entity/new/clean-lite-ps/use-cases/find-by-id.ejs.t +16 -0
  239. package/templates/entity/new/clean-lite-ps/use-cases/list.ejs.t +16 -0
  240. package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +7 -0
  241. package/templates/entity/new/frontend/_inject-entities-import.ejs.t +7 -0
  242. package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +10 -0
  243. package/templates/entity/new/frontend/collections/_inject-index.ejs.t +9 -0
  244. package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +9 -0
  245. package/templates/entity/new/frontend/collections/collection.ejs.t +61 -0
  246. package/templates/entity/new/frontend/collections/collections-base.ejs.t +24 -0
  247. package/templates/entity/new/frontend/entity/collection.ejs.t +172 -0
  248. package/templates/entity/new/frontend/entity/combined.ejs.t +474 -0
  249. package/templates/entity/new/frontend/entity/fields.ejs.t +104 -0
  250. package/templates/entity/new/frontend/entity/hooks.ejs.t +73 -0
  251. package/templates/entity/new/frontend/entity/index.ejs.t +21 -0
  252. package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +84 -0
  253. package/templates/entity/new/frontend/entity/mutations.ejs.t +38 -0
  254. package/templates/entity/new/frontend/entity/types.ejs.t +59 -0
  255. package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +7 -0
  256. package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +7 -0
  257. package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +7 -0
  258. package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +9 -0
  259. package/templates/entity/new/frontend/store/_inject-collections.ejs.t +9 -0
  260. package/templates/entity/new/frontend/store/_inject-entity.ejs.t +9 -0
  261. package/templates/entity/new/frontend/store/_inject-import.ejs.t +9 -0
  262. package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +9 -0
  263. package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +10 -0
  264. package/templates/entity/new/frontend/store/hooks.ejs.t +72 -0
  265. package/templates/entity/new/frontend/unified-entity.ejs.t +28 -0
  266. package/templates/entity/new/prompt.js +1421 -0
  267. package/templates/relationship/new/controller.ejs.t +36 -0
  268. package/templates/relationship/new/dto/create.ejs.t +41 -0
  269. package/templates/relationship/new/dto/output.ejs.t +44 -0
  270. package/templates/relationship/new/dto/update.ejs.t +10 -0
  271. package/templates/relationship/new/entity.ejs.t +98 -0
  272. package/templates/relationship/new/index.ejs.t +19 -0
  273. package/templates/relationship/new/module.ejs.t +35 -0
  274. package/templates/relationship/new/prompt.js +682 -0
  275. package/templates/relationship/new/repository.ejs.t +54 -0
  276. package/templates/relationship/new/service.ejs.t +31 -0
  277. package/templates/relationship/new/use-cases/declarative-queries.ejs.t +34 -0
  278. package/templates/relationship/new/use-cases/find-by-id.ejs.t +16 -0
  279. package/templates/relationship/new/use-cases/list.ejs.t +16 -0
@@ -0,0 +1,617 @@
1
+ /**
2
+ * Clean-Lite-PS template locals extension
3
+ *
4
+ * Exports buildCleanLitePsLocals(definition, baseLocals) which derives
5
+ * all variables required by the clean-lite-ps template set.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Family → Base Class Mapping
10
+ // ============================================================================
11
+
12
+ const FAMILY_MAP = {
13
+ 'synced': {
14
+ repositoryBaseClass: 'SyncedEntityRepository',
15
+ serviceBaseClass: 'SyncedEntityService',
16
+ repositoryBaseImport: '@shared/base-classes/synced-entity-repository',
17
+ serviceBaseImport: '@shared/base-classes/synced-entity-service',
18
+ repositoryInheritedMethods: [
19
+ 'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
20
+ 'findByExternalId, findAllByUserId, findVisibleByUserId, syncUpsert',
21
+ ],
22
+ serviceInheritedMethods: [
23
+ 'findById, findByIds, list, count, exists, create, update, delete',
24
+ 'findByExternalId, findAllByUserId, findVisibleByUserId',
25
+ ],
26
+ },
27
+ activity: {
28
+ repositoryBaseClass: 'ActivityEntityRepository',
29
+ serviceBaseClass: 'ActivityEntityService',
30
+ repositoryBaseImport: '@shared/base-classes/activity-entity-repository',
31
+ serviceBaseImport: '@shared/base-classes/activity-entity-service',
32
+ repositoryInheritedMethods: [
33
+ 'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
34
+ 'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
35
+ ],
36
+ serviceInheritedMethods: [
37
+ 'findById, findByIds, list, count, exists, create, update, delete',
38
+ 'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
39
+ ],
40
+ },
41
+ knowledge: {
42
+ repositoryBaseClass: 'KnowledgeEntityRepository',
43
+ serviceBaseClass: 'KnowledgeEntityService',
44
+ repositoryBaseImport: '@shared/base-classes/knowledge-entity-repository',
45
+ serviceBaseImport: '@shared/base-classes/knowledge-entity-service',
46
+ repositoryInheritedMethods: [
47
+ 'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
48
+ 'semanticSearch, findPendingByOpportunityId, updateStatus, updateStatusBatch',
49
+ ],
50
+ serviceInheritedMethods: [
51
+ 'findById, findByIds, list, count, exists, create, update, delete',
52
+ 'semanticSearch, findPendingByOpportunityId, updateStatus, updateStatusBatch',
53
+ ],
54
+ },
55
+ metadata: {
56
+ repositoryBaseClass: 'MetadataEntityRepository',
57
+ serviceBaseClass: 'MetadataEntityService',
58
+ repositoryBaseImport: '@shared/base-classes/metadata-entity-repository',
59
+ serviceBaseImport: '@shared/base-classes/metadata-entity-service',
60
+ repositoryInheritedMethods: [
61
+ 'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
62
+ 'findByEntityIdAndType, listByEntityId, listHistoryByEntityId',
63
+ ],
64
+ serviceInheritedMethods: [
65
+ 'findById, findByIds, list, count, exists, create, update, delete',
66
+ 'findByEntityIdAndType, listByEntityId, listHistoryByEntityId',
67
+ ],
68
+ },
69
+ base: {
70
+ repositoryBaseClass: 'BaseRepository',
71
+ serviceBaseClass: 'BaseService',
72
+ repositoryBaseImport: '@shared/base-classes/base-repository',
73
+ serviceBaseImport: '@shared/base-classes/base-service',
74
+ repositoryInheritedMethods: [
75
+ 'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
76
+ ],
77
+ serviceInheritedMethods: [
78
+ 'findById, findByIds, list, count, exists, create, update, delete',
79
+ ],
80
+ },
81
+ };
82
+
83
+ // ============================================================================
84
+ // Helper utilities
85
+ // ============================================================================
86
+
87
+ const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
88
+ const camelCase = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
89
+ const pascalCase = (s) => capitalize(camelCase(s));
90
+ const pluralize = (s) => {
91
+ if (s.endsWith('y')) return s.slice(0, -1) + 'ies';
92
+ if (s.endsWith('s') || s.endsWith('x') || s.endsWith('ch') || s.endsWith('sh'))
93
+ return s + 'es';
94
+ return s + 's';
95
+ };
96
+
97
+ // ============================================================================
98
+ // Drizzle type mapping
99
+ // ============================================================================
100
+
101
+ const DRIZZLE_TYPE_MAP = {
102
+ string: 'text',
103
+ integer: 'integer',
104
+ decimal: 'numeric',
105
+ boolean: 'boolean',
106
+ uuid: 'uuid',
107
+ date: 'date',
108
+ datetime: 'timestamp',
109
+ json: 'jsonb',
110
+ };
111
+
112
+ // Drizzle import name for each drizzle type
113
+ const DRIZZLE_IMPORT_MAP = {
114
+ text: 'text',
115
+ integer: 'integer',
116
+ numeric: 'numeric',
117
+ boolean: 'boolean',
118
+ uuid: 'uuid',
119
+ date: 'date',
120
+ timestamp: 'timestamp',
121
+ jsonb: 'jsonb',
122
+ };
123
+
124
+ // ============================================================================
125
+ // Zod type mapping
126
+ // ============================================================================
127
+
128
+ const ZOD_TYPE_MAP = {
129
+ string: 'z.string()',
130
+ integer: 'z.number().int()',
131
+ // PG numeric is returned by Drizzle as a string; z.coerce.number() parses
132
+ // strings at the boundary while still accepting numeric JSON input.
133
+ decimal: 'z.coerce.number()',
134
+ boolean: 'z.boolean()',
135
+ uuid: 'z.string().uuid()',
136
+ date: 'z.coerce.date()',
137
+ datetime: 'z.coerce.date()',
138
+ // jsonb has no schema guarantees and routinely holds arrays, objects, or
139
+ // scalars — z.unknown() preserves that. Use z.record(...) shaping in refine
140
+ // code at the consumer if stricter validation is needed.
141
+ json: 'z.unknown()',
142
+ };
143
+
144
+ // TypeScript type mapping
145
+ const TS_TYPE_MAP = {
146
+ string: 'string',
147
+ integer: 'number',
148
+ decimal: 'number',
149
+ boolean: 'boolean',
150
+ uuid: 'string',
151
+ date: 'Date',
152
+ datetime: 'Date',
153
+ json: 'unknown',
154
+ };
155
+
156
+ // Fields managed by behaviors — excluded from create DTO
157
+ const BEHAVIOR_MANAGED_FIELDS = new Set([
158
+ 'created_at',
159
+ 'updated_at',
160
+ 'deleted_at',
161
+ 'created_by',
162
+ 'updated_by',
163
+ 'valid_from',
164
+ 'valid_to',
165
+ 'is_active',
166
+ ]);
167
+
168
+ // ============================================================================
169
+ // Field processors
170
+ // ============================================================================
171
+
172
+ /**
173
+ * Build a Drizzle column chain for a field
174
+ */
175
+ function buildDrizzleChain(fieldName, field, drizzleType) {
176
+ const nullable = field.nullable ?? false;
177
+ const required = field.required ?? false;
178
+ const hasDefault = field.default !== undefined && field.default !== null;
179
+
180
+ let chain = `${drizzleType}('${fieldName}')`;
181
+
182
+ // Add .notNull() for non-nullable required fields
183
+ if (required && !nullable) {
184
+ chain += '.notNull()';
185
+ }
186
+
187
+ // Boolean defaults
188
+ if (drizzleType === 'boolean' && hasDefault) {
189
+ chain += `.default(${field.default})`;
190
+ }
191
+
192
+ // Timestamp defaults for datetime fields in behavior context handled separately
193
+ return chain;
194
+ }
195
+
196
+ /**
197
+ * Process entity fields into ProcessedField[]
198
+ */
199
+ function processFields(fields) {
200
+ const processed = [];
201
+
202
+ for (const [fieldName, field] of Object.entries(fields)) {
203
+ if (fieldName === 'id') continue;
204
+
205
+ const type = field.type || 'string';
206
+ const nullable = field.nullable ?? false;
207
+ const required = field.required ?? false;
208
+ const hasDefault = field.default !== undefined && field.default !== null;
209
+ const choices = field.choices;
210
+ const hasChoices = Array.isArray(choices) && choices.length > 0;
211
+
212
+ const drizzleType = DRIZZLE_TYPE_MAP[type] || 'text';
213
+ const tsType = hasChoices
214
+ ? choices.map((c) => `'${c}'`).join(' | ')
215
+ : (TS_TYPE_MAP[type] || 'unknown');
216
+ const zodType = hasChoices
217
+ ? `z.enum([${choices.map((c) => `'${c}'`).join(', ')}])`
218
+ : (ZOD_TYPE_MAP[type] || 'z.unknown()');
219
+
220
+ const drizzleChain = buildDrizzleChain(fieldName, field, drizzleType);
221
+
222
+ processed.push({
223
+ name: fieldName,
224
+ camelName: camelCase(fieldName),
225
+ type,
226
+ drizzleType,
227
+ zodType,
228
+ tsType,
229
+ nullable,
230
+ required,
231
+ hasDefault,
232
+ isPrimaryKey: false,
233
+ drizzleChain,
234
+ choices,
235
+ hasChoices,
236
+ });
237
+ }
238
+
239
+ return processed;
240
+ }
241
+
242
+ /**
243
+ * Process belongs_to relationships into BelongsToRelation[]
244
+ */
245
+ function processBelongsTo(relationships) {
246
+ if (!relationships) return [];
247
+
248
+ const result = [];
249
+
250
+ for (const [relName, rel] of Object.entries(relationships)) {
251
+ if (rel.type !== 'belongs_to') continue;
252
+
253
+ const target = rel.target;
254
+ const field = rel.foreign_key;
255
+ const nullable = rel.nullable ?? true;
256
+ const relatedPlural = pluralize(target);
257
+
258
+ result.push({
259
+ field,
260
+ camelField: camelCase(field),
261
+ relatedEntity: target,
262
+ relatedEntityPascal: pascalCase(target),
263
+ relatedTable: relatedPlural,
264
+ relatedPlural,
265
+ nullable,
266
+ importPath: `../${relatedPlural}/${target}.entity`,
267
+ });
268
+ }
269
+
270
+ return result;
271
+ }
272
+
273
+ /**
274
+ * Collect drizzle imports needed for entity fields
275
+ */
276
+ function collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete) {
277
+ const imports = new Set(['pgTable', 'uuid']);
278
+
279
+ for (const field of processedFields) {
280
+ const importName = DRIZZLE_IMPORT_MAP[field.drizzleType];
281
+ if (importName) imports.add(importName);
282
+ }
283
+
284
+ // FK uuid columns from belongs_to
285
+ if (belongsTo.length > 0) {
286
+ imports.add('uuid');
287
+ }
288
+
289
+ // Behavior imports
290
+ if (hasTimestamps || hasSoftDelete) {
291
+ imports.add('timestamp');
292
+ }
293
+
294
+ if (belongsTo.length > 0) {
295
+ imports.add('relations');
296
+ }
297
+
298
+ return Array.from(imports).sort();
299
+ }
300
+
301
+ /**
302
+ * Derive Zod chain for a field in create DTO context
303
+ */
304
+ function zodChainForCreate(field) {
305
+ const { type, nullable, required, hasDefault, hasChoices, choices } = field;
306
+
307
+ if (hasChoices) {
308
+ const base = `z.enum([${choices.map((c) => `'${c}'`).join(', ')}])`;
309
+ if (!required && !nullable) return base + '.optional()';
310
+ if (nullable) return base + '.nullable()';
311
+ return base;
312
+ }
313
+
314
+ let base = ZOD_TYPE_MAP[type] || 'z.unknown()';
315
+
316
+ if (type === 'boolean' && hasDefault) {
317
+ base += `.default(${field.default ?? false})`;
318
+ return base;
319
+ }
320
+
321
+ if (nullable) {
322
+ return base + '.nullable()';
323
+ }
324
+
325
+ if (!required) {
326
+ return base + '.optional()';
327
+ }
328
+
329
+ return base;
330
+ }
331
+
332
+ /**
333
+ * Derive Zod chain for a field in output DTO context
334
+ */
335
+ function zodChainForOutput(field) {
336
+ const { type, nullable, hasChoices, choices } = field;
337
+
338
+ if (hasChoices) {
339
+ const base = `z.enum([${choices.map((c) => `'${c}'`).join(', ')}])`;
340
+ if (nullable) return base + '.nullable()';
341
+ return base;
342
+ }
343
+
344
+ let base = ZOD_TYPE_MAP[type] || 'z.unknown()';
345
+
346
+ if (nullable) {
347
+ return base + '.nullable()';
348
+ }
349
+
350
+ return base;
351
+ }
352
+
353
+ // ============================================================================
354
+ // Query processing
355
+ // ============================================================================
356
+
357
+ /**
358
+ * Derive repository method name from a declarative query definition.
359
+ * E.g., { by: ['user_id'] } → 'findByUserId'
360
+ * { by: ['email'], unique: true } → 'findByEmail'
361
+ * { by: ['opportunity_id'], select: ['email'] } → 'findEmailsByOpportunityId'
362
+ */
363
+ function deriveQueryMethodName(query) {
364
+ const byFields = Array.isArray(query.by) ? query.by : [];
365
+ const selectFields = Array.isArray(query.select) ? query.select : [];
366
+
367
+ const byPart = byFields.map((f) => pascalCase(f)).join('And');
368
+
369
+ if (selectFields.length > 0) {
370
+ const selectPart = selectFields.map((f) => pascalCase(f)).join('And') + 's';
371
+ return `find${selectPart}By${byPart}`;
372
+ }
373
+
374
+ return `findBy${byPart}`;
375
+ }
376
+
377
+ /**
378
+ * Process declarative queries from YAML queries: block.
379
+ * Produces typed query metadata for template generation.
380
+ */
381
+ function processQueries(queriesBlock, processedFields, entityNamePascal) {
382
+ if (!queriesBlock || !Array.isArray(queriesBlock) || queriesBlock.length === 0) {
383
+ return [];
384
+ }
385
+
386
+ // Build field name → TS type lookup
387
+ const fieldTypeMap = {};
388
+ for (const pf of processedFields) {
389
+ fieldTypeMap[pf.name] = pf.tsType;
390
+ fieldTypeMap[pf.camelName] = pf.tsType;
391
+ }
392
+
393
+ return queriesBlock.map((q) => {
394
+ const byFields = Array.isArray(q.by) ? q.by : [];
395
+ const selectFields = Array.isArray(q.select) ? q.select : [];
396
+ const isUnique = q.unique ?? false;
397
+ const viaTable = q.via ?? null;
398
+
399
+ const params = byFields.map((f) => ({
400
+ name: f,
401
+ camelName: camelCase(f),
402
+ tsType: fieldTypeMap[f] || fieldTypeMap[camelCase(f)] || 'string',
403
+ }));
404
+
405
+ let orderBy = null;
406
+ let orderDirection = null;
407
+ if (q.order) {
408
+ const parts = q.order.trim().split(/\s+/);
409
+ orderBy = camelCase(parts[0]);
410
+ orderDirection = parts[1] || 'asc';
411
+ }
412
+
413
+ const methodName = deriveQueryMethodName(q);
414
+
415
+ let returnType;
416
+ if (isUnique) {
417
+ returnType = `${entityNamePascal} | null`;
418
+ } else if (selectFields.length > 0) {
419
+ const camelFields = selectFields.map((f) => camelCase(f));
420
+ returnType = selectFields.length === 1
421
+ ? `${fieldTypeMap[selectFields[0]] || fieldTypeMap[camelFields[0]] || 'string'}[]`
422
+ : `Pick<${entityNamePascal}, ${camelFields.map((f) => `'${f}'`).join(' | ')}>[]`;
423
+ } else {
424
+ returnType = `${entityNamePascal}[]`;
425
+ }
426
+
427
+ // Prefix class name with entity to guarantee uniqueness across modules.
428
+ // e.g. methodName 'findByDomain' on Account → 'FindAccountByDomainUseCase'
429
+ // methodName 'findEmailsByOpportunityId' on Contact → 'FindContactEmailsByOpportunityIdUseCase'
430
+ const methodPascal = pascalCase(methodName);
431
+ const useCaseClassName = methodPascal.replace(/^Find/, `Find${entityNamePascal}`) + 'UseCase';
432
+
433
+ return {
434
+ by: byFields,
435
+ unique: isUnique,
436
+ select: selectFields,
437
+ order: q.order ?? null,
438
+ limit: q.limit ?? null,
439
+ via: viaTable,
440
+ methodName,
441
+ returnType,
442
+ params,
443
+ isUnique,
444
+ orderBy,
445
+ orderDirection,
446
+ viaTable,
447
+ viaTableCamel: viaTable ? camelCase(viaTable) : null,
448
+ selectFields: selectFields.map((f) => camelCase(f)),
449
+ useCaseClassName,
450
+ hasVia: viaTable != null,
451
+ hasSelect: selectFields.length > 0,
452
+ hasOrder: q.order != null,
453
+ hasLimit: q.limit != null,
454
+ hasMultipleParams: params.length > 1,
455
+ };
456
+ });
457
+ }
458
+
459
+ // ============================================================================
460
+ // Main export
461
+ // ============================================================================
462
+
463
+ /**
464
+ * Build Clean-Lite-PS template locals from entity definition and base locals
465
+ *
466
+ * @param {object} definition - Parsed entity YAML
467
+ * @param {object} baseLocals - Locals from main prompt.js
468
+ * @returns {object} Merged locals with all clean-lite-ps variables
469
+ */
470
+ export function buildCleanLitePsLocals(definition, baseLocals) {
471
+ const entity = definition.entity;
472
+ const fields = definition.fields || {};
473
+ const relationships = definition.relationships || {};
474
+ const behaviors = definition.behaviors || [];
475
+ const queriesBlock = definition.queries || null;
476
+
477
+ // Source root — configurable via baseLocals.srcRoot or entity.src_root, defaults to 'src'
478
+ const srcRoot = baseLocals.srcRoot || entity.src_root || 'src';
479
+
480
+ const entityName = entity.name;
481
+ const entityNamePascal = pascalCase(entityName);
482
+ const entityNamePlural = entity.plural || pluralize(entityName);
483
+ const entityNamePluralPascal = pascalCase(entityNamePlural);
484
+
485
+ // Family resolution
486
+ const family = entity.family || 'base';
487
+ const familyConfig = FAMILY_MAP[family] || FAMILY_MAP['base'];
488
+
489
+ // Process entity fields
490
+ const processedFields = processFields(fields);
491
+
492
+ // Behavior flags (re-read from behaviors array for clean-lite-ps use)
493
+ const behaviorNames = behaviors.map((b) => (typeof b === 'string' ? b : b.name));
494
+ const hasTimestamps = behaviorNames.includes('timestamps');
495
+ const hasSoftDelete = behaviorNames.includes('soft_delete');
496
+
497
+ // Process declarative queries
498
+ const processedQueries = processQueries(queriesBlock, processedFields, entityNamePascal);
499
+ const hasDeclarativeQueries = processedQueries.length > 0;
500
+ const declarativeQueryClasses = processedQueries.map((q) => q.useCaseClassName);
501
+ const hasMultiFieldQuery = processedQueries.some((q) => q.hasMultipleParams);
502
+ const hasOrderedQuery = processedQueries.some((q) => q.hasOrder);
503
+ const hasViaQuery = processedQueries.some((q) => q.hasVia);
504
+
505
+ // Process belongs_to relationships
506
+ const belongsTo = processBelongsTo(relationships);
507
+
508
+ // Filter FK fields that are already emitted by the clpBelongsTo loop
509
+ const fkFieldNames = new Set(belongsTo.map((r) => r.field));
510
+ const nonFkFields = processedFields.filter((f) => !fkFieldNames.has(f.name));
511
+
512
+ // Drizzle imports needed
513
+ const drizzleEntityImports = collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete);
514
+ // Whether relations() import is needed
515
+ const hasRelationsBlock = belongsTo.length > 0;
516
+
517
+ // Output paths
518
+ const outputPaths = {
519
+ entity: `${srcRoot}/modules/${entityNamePlural}/${entityName}.entity.ts`,
520
+ repository: `${srcRoot}/modules/${entityNamePlural}/${entityName}.repository.ts`,
521
+ service: `${srcRoot}/modules/${entityNamePlural}/${entityName}.service.ts`,
522
+ controller: `${srcRoot}/modules/${entityNamePlural}/${entityName}.controller.ts`,
523
+ module: `${srcRoot}/modules/${entityNamePlural}/${entityNamePlural}.module.ts`,
524
+ findByIdUseCase: `${srcRoot}/modules/${entityNamePlural}/use-cases/find-${entityName}-by-id.use-case.ts`,
525
+ listUseCase: `${srcRoot}/modules/${entityNamePlural}/use-cases/list-${entityNamePlural}.use-case.ts`,
526
+ createDto: `${srcRoot}/modules/${entityNamePlural}/dto/create-${entityName}.dto.ts`,
527
+ updateDto: `${srcRoot}/modules/${entityNamePlural}/dto/update-${entityName}.dto.ts`,
528
+ outputDto: `${srcRoot}/modules/${entityNamePlural}/dto/${entityName}-output.dto.ts`,
529
+ declarativeQueries: hasDeclarativeQueries
530
+ ? `${srcRoot}/modules/${entityNamePlural}/use-cases/declarative-queries.ts`
531
+ : null,
532
+ };
533
+
534
+ // Class names
535
+ const classNames = {
536
+ entity: entityNamePascal,
537
+ entityTable: entityNamePlural,
538
+ repository: `${entityNamePascal}Repository`,
539
+ service: `${entityNamePascal}Service`,
540
+ controller: `${entityNamePascal}Controller`,
541
+ module: `${entityNamePluralPascal}Module`,
542
+ findByIdUseCase: `Find${entityNamePascal}ByIdUseCase`,
543
+ listUseCase: `List${entityNamePluralPascal}UseCase`,
544
+ createDto: `Create${entityNamePascal}Dto`,
545
+ updateDto: `Update${entityNamePascal}Dto`,
546
+ outputDto: `${entityNamePascal}OutputDto`,
547
+ createSchema: `Create${entityNamePascal}Schema`,
548
+ updateSchema: `Update${entityNamePascal}Schema`,
549
+ outputSchema: `${entityNamePascal}OutputSchema`,
550
+ };
551
+
552
+ // Fields for create DTO: exclude id, behavior-managed fields, and FK fields
553
+ const createDtoFields = nonFkFields.filter(
554
+ (f) => !BEHAVIOR_MANAGED_FIELDS.has(f.name),
555
+ );
556
+
557
+ // FK fields from belongs_to for create/output DTOs
558
+ const belongsToFkFields = belongsTo.map((rel) => ({
559
+ camelName: rel.camelField,
560
+ zodChainCreate: rel.nullable ? 'z.string().uuid().nullable()' : 'z.string().uuid()',
561
+ zodChainOutput: rel.nullable ? 'z.string().uuid().nullable()' : 'z.string().uuid()',
562
+ nullable: rel.nullable,
563
+ }));
564
+
565
+ // Build zodChain for each create DTO field
566
+ const createDtoFieldsWithZod = createDtoFields.map((f) => ({
567
+ ...f,
568
+ zodChainCreate: zodChainForCreate(f),
569
+ }));
570
+
571
+ // Build zodChain for each output DTO field (all non-FK fields)
572
+ const outputDtoFields = nonFkFields.map((f) => ({
573
+ ...f,
574
+ zodChainOutput: zodChainForOutput(f),
575
+ }));
576
+
577
+ return {
578
+ // Clean-Lite-PS identity
579
+ entityName,
580
+ entityNamePascal,
581
+ entityNamePlural,
582
+ entityNamePluralPascal,
583
+
584
+ // Family
585
+ family,
586
+ ...familyConfig,
587
+
588
+ // Behavior flags (also exposed at top level for template use)
589
+ hasTimestamps,
590
+ hasSoftDelete,
591
+
592
+ // Output paths
593
+ clpOutputPaths: outputPaths,
594
+
595
+ // Class names
596
+ classNames,
597
+
598
+ // Field data
599
+ clpProcessedFields: nonFkFields,
600
+ clpCreateDtoFields: createDtoFieldsWithZod,
601
+ clpOutputDtoFields: outputDtoFields,
602
+ clpBelongsTo: belongsTo,
603
+ clpBelongsToFkFields: belongsToFkFields,
604
+
605
+ // Drizzle
606
+ clpDrizzleImports: drizzleEntityImports,
607
+ clpHasRelationsBlock: hasRelationsBlock,
608
+
609
+ // Declarative queries
610
+ processedQueries,
611
+ hasDeclarativeQueries,
612
+ declarativeQueryClasses,
613
+ hasMultiFieldQuery,
614
+ hasOrderedQuery,
615
+ hasViaQuery,
616
+ };
617
+ }
@@ -0,0 +1,62 @@
1
+ ---
2
+ to: "<%= typeof clpOutputPaths !== 'undefined' ? clpOutputPaths.repository : null %>"
3
+ skip_if: "<%= typeof clpOutputPaths === 'undefined' %>"
4
+ force: true
5
+ ---
6
+ import { Injectable, Inject } from '@nestjs/common';
7
+ <% if (hasDeclarativeQueries) { -%>
8
+ import { eq<%= hasMultiFieldQuery ? ', and' : '' %><%= hasOrderedQuery ? ', desc, asc' : '' %> } from 'drizzle-orm';
9
+ <% } -%>
10
+ import { DRIZZLE } from '@shared/constants/tokens';
11
+ import type { DrizzleClient } from '@shared/types/drizzle';
12
+ import { <%= repositoryBaseClass %> } from '<%= repositoryBaseImport %>';
13
+ <% if (hasTimestamps || hasSoftDelete) { -%>
14
+ import type { BehaviorConfig } from '@shared/base-classes/base-repository';
15
+ <% } -%>
16
+ import { <%= entityNamePlural %>, type <%= classNames.entity %> } from './<%= entityName %>.entity';
17
+
18
+ @Injectable()
19
+ export class <%= classNames.repository %> extends <%= repositoryBaseClass %><<%= classNames.entity %>> {
20
+ readonly table = <%= entityNamePlural %>;
21
+ <% if (hasTimestamps || hasSoftDelete) { -%>
22
+
23
+ // Behaviors declared in YAML -> generated as config object
24
+ protected override readonly behaviors: BehaviorConfig = {
25
+ timestamps: <%= !!hasTimestamps %>,
26
+ softDelete: <%= !!hasSoftDelete %>,
27
+ userTracking: false,
28
+ };
29
+ <% } -%>
30
+
31
+ constructor(@Inject(DRIZZLE) db: DrizzleClient) {
32
+ super(db);
33
+ }
34
+ <% if (hasDeclarativeQueries) { -%>
35
+
36
+ // ═══════════════════════════════════════════════════════════════════════
37
+ // Declarative queries (from queries: block in entity YAML)
38
+ // ═══════════════════════════════════════════════════════════════════════
39
+ <%_ processedQueries.forEach((q) => { _%>
40
+
41
+ async <%= q.methodName %>(<%- q.params.map(p => `${p.camelName}: ${p.tsType}`).join(', ') %>): Promise<<%- q.returnType %>> {
42
+ <% if (q.isUnique) { -%>
43
+ const rows = await this.baseQuery()
44
+ .where(<%- q.hasMultipleParams ? 'and(' : '' %><%- q.params.map(p => `eq(this.table['${p.camelName}'], ${p.camelName})`).join(', ') %><%- q.hasMultipleParams ? ')' : '' %>)
45
+ .limit(1);
46
+ return (rows[0] as <%= classNames.entity %>) ?? null;
47
+ <% } else { -%>
48
+ const rows = await this.baseQuery()
49
+ .where(<%- q.hasMultipleParams ? 'and(' : '' %><%- q.params.map(p => `eq(this.table['${p.camelName}'], ${p.camelName})`).join(', ') %><%- q.hasMultipleParams ? ')' : '' %>)<%- q.hasOrder ? `.orderBy(${q.orderDirection}(this.table['${q.orderBy}']))` : '' %>;
50
+ return rows as <%= classNames.entity %>[];
51
+ <% } -%>
52
+ }
53
+ <%_ }) _%>
54
+ <% } else { -%>
55
+
56
+ // TODO: Add entity-specific query methods here.
57
+ <% } -%>
58
+ // Inherited from <%= repositoryBaseClass %>:
59
+ <%_ repositoryInheritedMethods.forEach(line => { _%>
60
+ // <%= line %>
61
+ <%_ }) _%>
62
+ }