@tailor-platform/erp-kit 0.1.1 → 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 (342) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +158 -62
  3. package/dist/cli.js +1010 -270
  4. package/package.json +11 -8
  5. package/schemas/module/command.yml +1 -0
  6. package/schemas/module/model.yml +14 -0
  7. package/schemas/module/query.yml +53 -0
  8. package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +2 -2
  9. package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +3 -3
  10. package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -2
  11. package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/SKILL.md +3 -3
  12. package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/SKILL.md +4 -4
  13. package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/SKILL.md +3 -3
  14. package/skills/{mock-scenario → erp-kit-mock-scenario}/SKILL.md +1 -1
  15. package/skills/{1-module-docs → erp-kit-module-1-docs}/SKILL.md +2 -2
  16. package/skills/{2-module-feature-breakdown → erp-kit-module-2-feature-breakdown}/SKILL.md +13 -9
  17. package/skills/erp-kit-module-2-feature-breakdown/references/naming.md +59 -0
  18. package/skills/{3-module-doc-review → erp-kit-module-3-doc-review}/SKILL.md +83 -25
  19. package/skills/erp-kit-module-4-tdd/SKILL.md +94 -0
  20. package/skills/erp-kit-module-4-tdd/references/cross-module-dependency.md +133 -0
  21. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/db-relations.md +5 -1
  22. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/exports.md +1 -1
  23. package/skills/erp-kit-module-4-tdd/references/generated-code.md +32 -0
  24. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/SKILL.md +46 -44
  25. package/skills/erp-kit-module-5-impl-review/references/commands.md +62 -0
  26. package/skills/erp-kit-module-5-impl-review/references/errors.md +10 -0
  27. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/references/testing.md +1 -1
  28. package/skills/erp-kit-module-shared/SKILL.md +16 -0
  29. package/skills/erp-kit-module-shared/references/commands.md +203 -0
  30. package/skills/erp-kit-module-shared/references/errors.md +35 -0
  31. package/skills/erp-kit-module-shared/references/queries.md +168 -0
  32. package/skills/erp-kit-module-shared/references/structure.md +36 -0
  33. package/skills/{3-module-doc-review → erp-kit-module-shared}/references/testing.md +4 -3
  34. package/skills/erp-kit-update/SKILL.md +64 -0
  35. package/src/cli.doc.test.ts +65 -0
  36. package/src/cli.ts +3 -117
  37. package/src/commands/app/index.ts +74 -0
  38. package/src/commands/check.test.ts +3 -2
  39. package/src/commands/check.ts +3 -2
  40. package/src/commands/index.ts +73 -0
  41. package/src/commands/init.test.ts +22 -5
  42. package/src/commands/init.ts +25 -16
  43. package/src/commands/license.ts +193 -0
  44. package/src/commands/mock/index.ts +2 -2
  45. package/src/commands/mock/start.ts +1 -1
  46. package/src/commands/mock/validate.test.ts +1 -1
  47. package/src/commands/module/generate.ts +35 -0
  48. package/src/commands/module/index.ts +87 -0
  49. package/src/commands/module/list.test.ts +57 -0
  50. package/src/commands/module/list.ts +64 -0
  51. package/src/commands/scaffold-templates.ts +65 -0
  52. package/src/commands/scaffold.test.ts +97 -2
  53. package/src/commands/scaffold.ts +24 -3
  54. package/src/commands/sync-check.test.ts +88 -1
  55. package/src/commands/sync-check.ts +41 -2
  56. package/src/generator/generate-code.test.ts +200 -0
  57. package/src/generator/generate-code.ts +260 -0
  58. package/src/generator/parse-command-doc.test.ts +159 -0
  59. package/src/generator/parse-command-doc.ts +116 -0
  60. package/src/integration.test.ts +6 -8
  61. package/src/module.ts +10 -9
  62. package/src/modules/item-management/README.md +38 -0
  63. package/src/modules/item-management/command/activateItem.generated.ts +6 -0
  64. package/src/modules/item-management/command/activateItem.test.ts +76 -0
  65. package/src/modules/item-management/command/activateItem.ts +42 -0
  66. package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +6 -0
  67. package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +88 -0
  68. package/src/modules/item-management/command/assignItemToTaxonomy.ts +63 -0
  69. package/src/modules/item-management/command/createItem.generated.ts +6 -0
  70. package/src/modules/item-management/command/createItem.test.ts +152 -0
  71. package/src/modules/item-management/command/createItem.ts +72 -0
  72. package/src/modules/item-management/command/createTaxonomyNode.generated.ts +6 -0
  73. package/src/modules/item-management/command/createTaxonomyNode.test.ts +126 -0
  74. package/src/modules/item-management/command/createTaxonomyNode.ts +70 -0
  75. package/src/modules/item-management/command/deactivateItem.generated.ts +6 -0
  76. package/src/modules/item-management/command/deactivateItem.test.ts +76 -0
  77. package/src/modules/item-management/command/deactivateItem.ts +42 -0
  78. package/src/modules/item-management/command/deleteItem.generated.ts +6 -0
  79. package/src/modules/item-management/command/deleteItem.test.ts +61 -0
  80. package/src/modules/item-management/command/deleteItem.ts +38 -0
  81. package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +6 -0
  82. package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +73 -0
  83. package/src/modules/item-management/command/deleteTaxonomyNode.ts +50 -0
  84. package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +6 -0
  85. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +136 -0
  86. package/src/modules/item-management/command/moveTaxonomyNode.ts +85 -0
  87. package/src/modules/item-management/command/reactivateItem.generated.ts +6 -0
  88. package/src/modules/item-management/command/reactivateItem.test.ts +76 -0
  89. package/src/modules/item-management/command/reactivateItem.ts +42 -0
  90. package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +6 -0
  91. package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +43 -0
  92. package/src/modules/item-management/command/removeItemFromTaxonomy.ts +30 -0
  93. package/src/modules/item-management/command/updateItem.generated.ts +6 -0
  94. package/src/modules/item-management/command/updateItem.test.ts +178 -0
  95. package/src/modules/item-management/command/updateItem.ts +103 -0
  96. package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +6 -0
  97. package/src/modules/item-management/command/updateTaxonomyNode.test.ts +88 -0
  98. package/src/modules/item-management/command/updateTaxonomyNode.ts +62 -0
  99. package/src/modules/item-management/db/item.ts +47 -0
  100. package/src/modules/item-management/db/itemTaxonomyAssignment.ts +49 -0
  101. package/src/modules/item-management/db/taxonomyNode.ts +34 -0
  102. package/src/modules/item-management/docs/commands/ActivateItem.md +32 -0
  103. package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +38 -0
  104. package/src/modules/item-management/docs/commands/CreateItem.md +44 -0
  105. package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +44 -0
  106. package/src/modules/item-management/docs/commands/DeactivateItem.md +34 -0
  107. package/src/modules/item-management/docs/commands/DeleteItem.md +35 -0
  108. package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +39 -0
  109. package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +45 -0
  110. package/src/modules/item-management/docs/commands/ReactivateItem.md +34 -0
  111. package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +30 -0
  112. package/src/modules/item-management/docs/commands/UpdateItem.md +55 -0
  113. package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +36 -0
  114. package/src/modules/item-management/docs/features/item-lifecycle.md +60 -0
  115. package/src/modules/item-management/docs/features/item-taxonomy.md +65 -0
  116. package/src/modules/item-management/docs/models/ItemTaxonomyAssignment.md +36 -0
  117. package/src/modules/item-management/docs/models/TaxonomyNode.md +47 -0
  118. package/src/modules/item-management/docs/models/item.md +59 -0
  119. package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +36 -0
  120. package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +40 -0
  121. package/src/modules/item-management/docs/queries/DetectCircularReference.md +41 -0
  122. package/src/modules/item-management/docs/queries/GetItem.md +38 -0
  123. package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +29 -0
  124. package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +35 -0
  125. package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +29 -0
  126. package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +29 -0
  127. package/src/modules/item-management/generated/enums.ts +9 -0
  128. package/src/modules/item-management/generated/kysely-tailordb.ts +62 -0
  129. package/src/modules/item-management/index.ts +53 -0
  130. package/src/modules/item-management/lib/_db_deps.ts +13 -0
  131. package/src/modules/item-management/lib/errors.generated.ts +117 -0
  132. package/src/modules/item-management/lib/permissions.generated.ts +17 -0
  133. package/src/modules/item-management/lib/types.ts +19 -0
  134. package/src/modules/item-management/module.ts +97 -0
  135. package/src/modules/item-management/query/calculateNodeDepth.generated.ts +5 -0
  136. package/src/modules/item-management/query/calculateNodeDepth.test.ts +56 -0
  137. package/src/modules/item-management/query/calculateNodeDepth.ts +28 -0
  138. package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +5 -0
  139. package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +75 -0
  140. package/src/modules/item-management/query/calculateSubtreeDepth.ts +29 -0
  141. package/src/modules/item-management/query/detectCircularReference.generated.ts +5 -0
  142. package/src/modules/item-management/query/detectCircularReference.test.ts +61 -0
  143. package/src/modules/item-management/query/detectCircularReference.ts +32 -0
  144. package/src/modules/item-management/query/getItem.generated.ts +5 -0
  145. package/src/modules/item-management/query/getItem.test.ts +67 -0
  146. package/src/modules/item-management/query/getItem.ts +20 -0
  147. package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +5 -0
  148. package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +25 -0
  149. package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +18 -0
  150. package/src/modules/item-management/query/getTaxonomyNode.generated.ts +5 -0
  151. package/src/modules/item-management/query/getTaxonomyNode.test.ts +47 -0
  152. package/src/modules/item-management/query/getTaxonomyNode.ts +18 -0
  153. package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +5 -0
  154. package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +25 -0
  155. package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +16 -0
  156. package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +5 -0
  157. package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +34 -0
  158. package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +16 -0
  159. package/src/modules/item-management/tailor.config.ts +11 -0
  160. package/src/modules/item-management/testing/fixtures.ts +81 -0
  161. package/src/modules/primitives/command/activateCategory.generated.ts +6 -0
  162. package/src/modules/primitives/command/activateCategory.test.ts +11 -29
  163. package/src/modules/primitives/command/activateCategory.ts +27 -34
  164. package/src/modules/primitives/command/activateCurrency.generated.ts +6 -0
  165. package/src/modules/primitives/command/activateCurrency.test.ts +11 -29
  166. package/src/modules/primitives/command/activateCurrency.ts +27 -34
  167. package/src/modules/primitives/command/activateUnit.generated.ts +6 -0
  168. package/src/modules/primitives/command/activateUnit.test.ts +11 -15
  169. package/src/modules/primitives/command/activateUnit.ts +27 -34
  170. package/src/modules/primitives/command/createCategory.generated.ts +6 -0
  171. package/src/modules/primitives/command/createCategory.test.ts +27 -39
  172. package/src/modules/primitives/command/createCategory.ts +53 -62
  173. package/src/modules/primitives/command/createCurrency.generated.ts +6 -0
  174. package/src/modules/primitives/command/createCurrency.test.ts +78 -71
  175. package/src/modules/primitives/command/createCurrency.ts +43 -48
  176. package/src/modules/primitives/command/createExchangeRate.generated.ts +6 -0
  177. package/src/modules/primitives/command/createExchangeRate.test.ts +101 -100
  178. package/src/modules/primitives/command/createExchangeRate.ts +50 -59
  179. package/src/modules/primitives/command/createUnit.generated.ts +6 -0
  180. package/src/modules/primitives/command/createUnit.test.ts +92 -95
  181. package/src/modules/primitives/command/createUnit.ts +54 -57
  182. package/src/modules/primitives/command/deactivateCategory.generated.ts +6 -0
  183. package/src/modules/primitives/command/deactivateCategory.test.ts +27 -28
  184. package/src/modules/primitives/command/deactivateCategory.ts +43 -50
  185. package/src/modules/primitives/command/deactivateCurrency.generated.ts +6 -0
  186. package/src/modules/primitives/command/deactivateCurrency.test.ts +23 -38
  187. package/src/modules/primitives/command/deactivateCurrency.ts +31 -38
  188. package/src/modules/primitives/command/deactivateUnit.generated.ts +6 -0
  189. package/src/modules/primitives/command/deactivateUnit.test.ts +27 -23
  190. package/src/modules/primitives/command/deactivateUnit.ts +39 -49
  191. package/src/modules/primitives/command/setBaseCurrency.generated.ts +6 -0
  192. package/src/modules/primitives/command/setBaseCurrency.test.ts +40 -33
  193. package/src/modules/primitives/command/setBaseCurrency.ts +43 -50
  194. package/src/modules/primitives/command/setReferenceUnit.generated.ts +6 -0
  195. package/src/modules/primitives/command/setReferenceUnit.test.ts +39 -35
  196. package/src/modules/primitives/command/setReferenceUnit.ts +46 -59
  197. package/src/modules/primitives/db/unit.ts +13 -3
  198. package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -2
  199. package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -2
  200. package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -2
  201. package/src/modules/primitives/docs/commands/CreateCategory.md +1 -4
  202. package/src/modules/primitives/docs/commands/CreateCurrency.md +3 -4
  203. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +4 -5
  204. package/src/modules/primitives/docs/commands/CreateUnit.md +5 -5
  205. package/src/modules/primitives/docs/commands/DeactivateCategory.md +2 -3
  206. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +2 -3
  207. package/src/modules/primitives/docs/commands/DeactivateUnit.md +2 -3
  208. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +2 -3
  209. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +2 -3
  210. package/src/modules/primitives/docs/models/Currency.md +4 -0
  211. package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
  212. package/src/modules/primitives/docs/models/Unit.md +4 -1
  213. package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
  214. package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +3 -5
  215. package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +3 -5
  216. package/src/modules/primitives/docs/queries/GetBaseCurrency.md +32 -0
  217. package/src/modules/primitives/docs/queries/GetCurrency.md +36 -0
  218. package/src/modules/primitives/docs/queries/GetUnit.md +36 -0
  219. package/src/modules/primitives/docs/queries/GetUoMCategory.md +36 -0
  220. package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +26 -0
  221. package/src/modules/primitives/generated/kysely-tailordb.ts +24 -45
  222. package/src/modules/primitives/index.ts +17 -6
  223. package/src/modules/primitives/lib/errors.generated.ts +112 -0
  224. package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -10
  225. package/src/modules/primitives/module.ts +39 -27
  226. package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
  227. package/src/modules/primitives/{command → query}/convertAmount.test.ts +4 -21
  228. package/src/modules/primitives/query/convertAmount.ts +121 -0
  229. package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
  230. package/src/modules/primitives/{command → query}/convertQuantity.test.ts +8 -15
  231. package/src/modules/primitives/query/convertQuantity.ts +63 -0
  232. package/src/modules/primitives/query/getBaseCurrency.generated.ts +5 -0
  233. package/src/modules/primitives/query/getBaseCurrency.test.ts +28 -0
  234. package/src/modules/primitives/query/getBaseCurrency.ts +16 -0
  235. package/src/modules/primitives/query/getCurrency.generated.ts +5 -0
  236. package/src/modules/primitives/query/getCurrency.test.ts +47 -0
  237. package/src/modules/primitives/query/getCurrency.ts +18 -0
  238. package/src/modules/primitives/query/getUnit.generated.ts +5 -0
  239. package/src/modules/primitives/query/getUnit.test.ts +47 -0
  240. package/src/modules/primitives/query/getUnit.ts +18 -0
  241. package/src/modules/primitives/query/getUoMCategory.generated.ts +5 -0
  242. package/src/modules/primitives/query/getUoMCategory.test.ts +47 -0
  243. package/src/modules/primitives/query/getUoMCategory.ts +18 -0
  244. package/src/modules/primitives/query/listUnitsByCategory.generated.ts +5 -0
  245. package/src/modules/primitives/query/listUnitsByCategory.ts +16 -0
  246. package/src/modules/primitives/tailor.config.ts +3 -3
  247. package/src/modules/shared/defineCommand.test.ts +23 -10
  248. package/src/modules/shared/defineCommand.ts +23 -10
  249. package/src/modules/shared/defineQuery.test.ts +28 -0
  250. package/src/modules/shared/defineQuery.ts +16 -0
  251. package/src/modules/shared/internal.ts +3 -1
  252. package/src/modules/shared/requirePermission.test.ts +22 -21
  253. package/src/modules/shared/requirePermission.ts +8 -2
  254. package/src/modules/shared/result.ts +12 -0
  255. package/src/modules/shared/types.ts +8 -0
  256. package/src/modules/testing/index.ts +36 -11
  257. package/src/modules/user-management/command/activateUser.generated.ts +6 -0
  258. package/src/modules/user-management/command/activateUser.test.ts +27 -27
  259. package/src/modules/user-management/command/activateUser.ts +40 -48
  260. package/src/modules/user-management/command/assignPermissionToRole.generated.ts +6 -0
  261. package/src/modules/user-management/command/assignPermissionToRole.test.ts +42 -43
  262. package/src/modules/user-management/command/assignPermissionToRole.ts +59 -62
  263. package/src/modules/user-management/command/assignRoleToUser.generated.ts +6 -0
  264. package/src/modules/user-management/command/assignRoleToUser.test.ts +70 -63
  265. package/src/modules/user-management/command/assignRoleToUser.ts +63 -66
  266. package/src/modules/user-management/command/createPermission.generated.ts +6 -0
  267. package/src/modules/user-management/command/createPermission.test.ts +45 -38
  268. package/src/modules/user-management/command/createPermission.ts +42 -46
  269. package/src/modules/user-management/command/createRole.generated.ts +6 -0
  270. package/src/modules/user-management/command/createRole.test.ts +30 -29
  271. package/src/modules/user-management/command/createRole.ts +33 -33
  272. package/src/modules/user-management/command/createUser.generated.ts +6 -0
  273. package/src/modules/user-management/command/createUser.test.ts +64 -42
  274. package/src/modules/user-management/command/createUser.ts +54 -56
  275. package/src/modules/user-management/command/deactivateUser.generated.ts +6 -0
  276. package/src/modules/user-management/command/deactivateUser.test.ts +27 -27
  277. package/src/modules/user-management/command/deactivateUser.ts +40 -48
  278. package/src/modules/user-management/command/logAuditEvent.generated.ts +6 -0
  279. package/src/modules/user-management/command/logAuditEvent.test.ts +50 -42
  280. package/src/modules/user-management/command/logAuditEvent.ts +25 -28
  281. package/src/modules/user-management/command/reactivateUser.generated.ts +6 -0
  282. package/src/modules/user-management/command/reactivateUser.test.ts +31 -27
  283. package/src/modules/user-management/command/reactivateUser.ts +40 -48
  284. package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +6 -0
  285. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +52 -51
  286. package/src/modules/user-management/command/revokePermissionFromRole.ts +60 -57
  287. package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +6 -0
  288. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +53 -48
  289. package/src/modules/user-management/command/revokeRoleFromUser.ts +58 -57
  290. package/src/modules/user-management/docs/commands/CreatePermission.md +2 -2
  291. package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
  292. package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
  293. package/src/modules/user-management/docs/models/Permission.md +2 -0
  294. package/src/modules/user-management/docs/models/Role.md +2 -0
  295. package/src/modules/user-management/docs/models/RolePermission.md +2 -0
  296. package/src/modules/user-management/docs/models/User.md +2 -0
  297. package/src/modules/user-management/docs/models/UserRole.md +2 -0
  298. package/src/modules/user-management/generated/enums.ts +11 -11
  299. package/src/modules/user-management/generated/kysely-tailordb.ts +27 -56
  300. package/src/modules/user-management/index.ts +2 -2
  301. package/src/modules/user-management/lib/errors.generated.ts +67 -0
  302. package/src/modules/user-management/{permissions.ts → lib/permissions.generated.ts} +8 -7
  303. package/src/modules/user-management/module.ts +22 -22
  304. package/src/modules/user-management/tailor.config.ts +3 -3
  305. package/src/schemas.ts +2 -1
  306. package/skills/1-module-docs/references/structure.md +0 -22
  307. package/skills/2-module-feature-breakdown/references/commands.md +0 -48
  308. package/skills/2-module-feature-breakdown/references/structure.md +0 -22
  309. package/skills/3-module-doc-review/references/commands.md +0 -54
  310. package/skills/3-module-doc-review/references/models.md +0 -29
  311. package/skills/4-module-tdd-implementation/SKILL.md +0 -74
  312. package/skills/4-module-tdd-implementation/references/commands.md +0 -45
  313. package/skills/4-module-tdd-implementation/references/errors.md +0 -7
  314. package/skills/4-module-tdd-implementation/references/models.md +0 -30
  315. package/skills/4-module-tdd-implementation/references/structure.md +0 -22
  316. package/skills/4-module-tdd-implementation/references/testing.md +0 -37
  317. package/skills/5-module-implementation-review/references/commands.md +0 -45
  318. package/skills/5-module-implementation-review/references/errors.md +0 -7
  319. package/skills/5-module-implementation-review/references/exports.md +0 -8
  320. package/skills/5-module-implementation-review/references/models.md +0 -30
  321. package/src/modules/primitives/command/convertAmount.ts +0 -126
  322. package/src/modules/primitives/command/convertQuantity.ts +0 -73
  323. package/src/modules/primitives/lib/errors.ts +0 -138
  324. package/src/modules/user-management/lib/errors.ts +0 -81
  325. /package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/references/structure.md +0 -0
  326. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-detailview.md +0 -0
  327. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-form.md +0 -0
  328. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-listview.md +0 -0
  329. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/structure.md +0 -0
  330. /package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/references/structure.md +0 -0
  331. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/component.md +0 -0
  332. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-detailview.md +0 -0
  333. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-form.md +0 -0
  334. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-listview.md +0 -0
  335. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/structure.md +0 -0
  336. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/component.md +0 -0
  337. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-detailview.md +0 -0
  338. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-form.md +0 -0
  339. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-listview.md +0 -0
  340. /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/auth.md +0 -0
  341. /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/structure.md +0 -0
  342. /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
@@ -0,0 +1,63 @@
1
+ import type { ReadonlyDB, QueryContext } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+ import {
4
+ InactiveUnitError,
5
+ IncompatibleUnitsError,
6
+ UnitNotFoundError,
7
+ } from "../lib/errors.generated";
8
+ import { getUnit } from "./getUnit.generated";
9
+
10
+ export interface ConvertQuantityInput {
11
+ quantity: number;
12
+ sourceUnitSymbol: string;
13
+ targetUnitSymbol: string;
14
+ }
15
+
16
+ /**
17
+ * Converts a quantity from one unit of measure to another within the same category.
18
+ * Units are identified by their symbol (e.g., "kg", "lb", "g").
19
+ * The conversion uses each unit's conversion factor relative to the category's reference unit.
20
+ * Result is rounded to the target unit's precision setting.
21
+ */
22
+ export async function run(db: ReadonlyDB<DB>, input: ConvertQuantityInput, ctx: QueryContext) {
23
+ // Validate source unit exists
24
+ const { unit: sourceUnit } = await getUnit(db, { symbol: input.sourceUnitSymbol }, ctx);
25
+
26
+ if (!sourceUnit) {
27
+ throw new UnitNotFoundError(input.sourceUnitSymbol);
28
+ }
29
+
30
+ // Validate target unit exists
31
+ const { unit: targetUnit } = await getUnit(db, { symbol: input.targetUnitSymbol }, ctx);
32
+
33
+ if (!targetUnit) {
34
+ throw new UnitNotFoundError(input.targetUnitSymbol);
35
+ }
36
+
37
+ // Validate both units are active
38
+ if (!sourceUnit.isActive) {
39
+ throw new InactiveUnitError(input.sourceUnitSymbol);
40
+ }
41
+
42
+ if (!targetUnit.isActive) {
43
+ throw new InactiveUnitError(input.targetUnitSymbol);
44
+ }
45
+
46
+ // Validate units belong to the same category
47
+ if (sourceUnit.categoryId !== targetUnit.categoryId) {
48
+ throw new IncompatibleUnitsError(`${input.sourceUnitSymbol} and ${input.targetUnitSymbol}`);
49
+ }
50
+
51
+ // Perform conversion: result = quantity * sourceConversionFactor / targetConversionFactor
52
+ const rawResult = (input.quantity * sourceUnit.conversionFactor) / targetUnit.conversionFactor;
53
+
54
+ // Apply rounding to target unit's precision
55
+ const roundingFactor = Math.pow(10, targetUnit.roundingPrecision);
56
+ const convertedQuantity = Math.round(rawResult * roundingFactor) / roundingFactor;
57
+
58
+ return {
59
+ convertedQuantity,
60
+ sourceUnit,
61
+ targetUnit,
62
+ };
63
+ }
@@ -0,0 +1,5 @@
1
+ // @generated — do not edit
2
+ import { defineQuery } from "../../shared/internal";
3
+ import { run } from "./getBaseCurrency";
4
+
5
+ export const getBaseCurrency = defineQuery(run);
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../testing/index";
3
+ import type { DB } from "../generated/kysely-tailordb";
4
+ import type { QueryContext } from "../../shared/internal";
5
+ import { baseCurrencyUSD } from "../testing/fixtures";
6
+ import { getBaseCurrency } from "./getBaseCurrency.generated";
7
+
8
+ describe("getBaseCurrency", () => {
9
+ const ctx: QueryContext = { actorId: "test-actor" };
10
+
11
+ it("returns base currency when set", async () => {
12
+ const { db, spies } = createMockDb<DB>();
13
+ spies.select.mockReturnValue(baseCurrencyUSD);
14
+
15
+ const result = await getBaseCurrency(db, undefined, ctx);
16
+
17
+ expect(result.currency).toEqual(baseCurrencyUSD);
18
+ });
19
+
20
+ it("returns null when no base currency is set", async () => {
21
+ const { db, spies } = createMockDb<DB>();
22
+ spies.select.mockReturnValue(undefined);
23
+
24
+ const result = await getBaseCurrency(db, undefined, ctx);
25
+
26
+ expect(result.currency).toBeNull();
27
+ });
28
+ });
@@ -0,0 +1,16 @@
1
+ import type { ReadonlyDB } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ /**
5
+ * Retrieves the currency designated as the base currency.
6
+ * Returns null if no base currency has been set.
7
+ */
8
+ export async function run(db: ReadonlyDB<DB>) {
9
+ const currency = await db
10
+ .selectFrom("Currency")
11
+ .selectAll()
12
+ .where("isBaseCurrency", "=", true)
13
+ .executeTakeFirst();
14
+
15
+ return { currency: currency ?? null };
16
+ }
@@ -0,0 +1,5 @@
1
+ // @generated — do not edit
2
+ import { defineQuery } from "../../shared/internal";
3
+ import { run } from "./getCurrency";
4
+
5
+ export const getCurrency = defineQuery(run);
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../testing/index";
3
+ import type { DB } from "../generated/kysely-tailordb";
4
+ import { baseCurrencyUSD } from "../testing/fixtures";
5
+ import { run } from "./getCurrency";
6
+
7
+ describe("getCurrency", () => {
8
+ describe("by id", () => {
9
+ it("returns currency when found", async () => {
10
+ const { db, spies } = createMockDb<DB>();
11
+ spies.select.mockReturnValue(baseCurrencyUSD);
12
+
13
+ const result = await run(db, { id: "currency-usd" });
14
+
15
+ expect(result.currency).toEqual(baseCurrencyUSD);
16
+ });
17
+
18
+ it("returns null when currency not found", async () => {
19
+ const { db, spies } = createMockDb<DB>();
20
+ spies.select.mockReturnValue(undefined);
21
+
22
+ const result = await run(db, { id: "nonexistent" });
23
+
24
+ expect(result.currency).toBeNull();
25
+ });
26
+ });
27
+
28
+ describe("by code", () => {
29
+ it("returns currency when found", async () => {
30
+ const { db, spies } = createMockDb<DB>();
31
+ spies.select.mockReturnValue(baseCurrencyUSD);
32
+
33
+ const result = await run(db, { code: "USD" });
34
+
35
+ expect(result.currency).toEqual(baseCurrencyUSD);
36
+ });
37
+
38
+ it("returns null when currency not found", async () => {
39
+ const { db, spies } = createMockDb<DB>();
40
+ spies.select.mockReturnValue(undefined);
41
+
42
+ const result = await run(db, { code: "NONEXISTENT" });
43
+
44
+ expect(result.currency).toBeNull();
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,18 @@
1
+ import type { ReadonlyDB } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ export type GetCurrencyInput = { id: string } | { code: string };
5
+
6
+ export async function run(db: ReadonlyDB<DB>, input: GetCurrencyInput) {
7
+ let query = db.selectFrom("Currency").selectAll();
8
+
9
+ if ("id" in input) {
10
+ query = query.where("id", "=", input.id);
11
+ } else {
12
+ query = query.where("code", "=", input.code);
13
+ }
14
+
15
+ const currency = await query.executeTakeFirst();
16
+
17
+ return { currency: currency ?? null };
18
+ }
@@ -0,0 +1,5 @@
1
+ // @generated — do not edit
2
+ import { defineQuery } from "../../shared/internal";
3
+ import { run } from "./getUnit";
4
+
5
+ export const getUnit = defineQuery(run);
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../testing/index";
3
+ import type { DB } from "../generated/kysely-tailordb";
4
+ import { baseUnitKg } from "../testing/fixtures";
5
+ import { run } from "./getUnit";
6
+
7
+ describe("getUnit", () => {
8
+ describe("by id", () => {
9
+ it("returns unit when found", async () => {
10
+ const { db, spies } = createMockDb<DB>();
11
+ spies.select.mockReturnValue(baseUnitKg);
12
+
13
+ const result = await run(db, { id: "unit-kg" });
14
+
15
+ expect(result.unit).toEqual(baseUnitKg);
16
+ });
17
+
18
+ it("returns null when unit not found", async () => {
19
+ const { db, spies } = createMockDb<DB>();
20
+ spies.select.mockReturnValue(undefined);
21
+
22
+ const result = await run(db, { id: "nonexistent" });
23
+
24
+ expect(result.unit).toBeNull();
25
+ });
26
+ });
27
+
28
+ describe("by symbol", () => {
29
+ it("returns unit when found", async () => {
30
+ const { db, spies } = createMockDb<DB>();
31
+ spies.select.mockReturnValue(baseUnitKg);
32
+
33
+ const result = await run(db, { symbol: "kg" });
34
+
35
+ expect(result.unit).toEqual(baseUnitKg);
36
+ });
37
+
38
+ it("returns null when unit not found", async () => {
39
+ const { db, spies } = createMockDb<DB>();
40
+ spies.select.mockReturnValue(undefined);
41
+
42
+ const result = await run(db, { symbol: "nonexistent" });
43
+
44
+ expect(result.unit).toBeNull();
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,18 @@
1
+ import type { ReadonlyDB } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ export type GetUnitInput = { id: string } | { symbol: string };
5
+
6
+ export async function run(db: ReadonlyDB<DB>, input: GetUnitInput) {
7
+ let query = db.selectFrom("Unit").selectAll();
8
+
9
+ if ("id" in input) {
10
+ query = query.where("id", "=", input.id);
11
+ } else {
12
+ query = query.where("symbol", "=", input.symbol);
13
+ }
14
+
15
+ const unit = await query.executeTakeFirst();
16
+
17
+ return { unit: unit ?? null };
18
+ }
@@ -0,0 +1,5 @@
1
+ // @generated — do not edit
2
+ import { defineQuery } from "../../shared/internal";
3
+ import { run } from "./getUoMCategory";
4
+
5
+ export const getUoMCategory = defineQuery(run);
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../testing/index";
3
+ import type { DB } from "../generated/kysely-tailordb";
4
+ import { baseUoMCategory } from "../testing/fixtures";
5
+ import { run } from "./getUoMCategory";
6
+
7
+ describe("getUoMCategory", () => {
8
+ describe("by id", () => {
9
+ it("returns category when found", async () => {
10
+ const { db, spies } = createMockDb<DB>();
11
+ spies.select.mockReturnValue(baseUoMCategory);
12
+
13
+ const result = await run(db, { id: "category-1" });
14
+
15
+ expect(result.uoMCategory).toEqual(baseUoMCategory);
16
+ });
17
+
18
+ it("returns null when category not found", async () => {
19
+ const { db, spies } = createMockDb<DB>();
20
+ spies.select.mockReturnValue(undefined);
21
+
22
+ const result = await run(db, { id: "nonexistent" });
23
+
24
+ expect(result.uoMCategory).toBeNull();
25
+ });
26
+ });
27
+
28
+ describe("by name", () => {
29
+ it("returns category when found", async () => {
30
+ const { db, spies } = createMockDb<DB>();
31
+ spies.select.mockReturnValue(baseUoMCategory);
32
+
33
+ const result = await run(db, { name: "Weight" });
34
+
35
+ expect(result.uoMCategory).toEqual(baseUoMCategory);
36
+ });
37
+
38
+ it("returns null when category not found", async () => {
39
+ const { db, spies } = createMockDb<DB>();
40
+ spies.select.mockReturnValue(undefined);
41
+
42
+ const result = await run(db, { name: "Nonexistent" });
43
+
44
+ expect(result.uoMCategory).toBeNull();
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,18 @@
1
+ import type { ReadonlyDB } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ export type GetUoMCategoryInput = { id: string } | { name: string };
5
+
6
+ export async function run(db: ReadonlyDB<DB>, input: GetUoMCategoryInput) {
7
+ let query = db.selectFrom("UoMCategory").selectAll();
8
+
9
+ if ("id" in input) {
10
+ query = query.where("id", "=", input.id);
11
+ } else {
12
+ query = query.where("name", "=", input.name);
13
+ }
14
+
15
+ const uoMCategory = await query.executeTakeFirst();
16
+
17
+ return { uoMCategory: uoMCategory ?? null };
18
+ }
@@ -0,0 +1,5 @@
1
+ // @generated — do not edit
2
+ import { defineQuery } from "../../shared/internal";
3
+ import { run } from "./listUnitsByCategory";
4
+
5
+ export const listUnitsByCategory = defineQuery(run);
@@ -0,0 +1,16 @@
1
+ import type { ReadonlyDB } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ export interface ListUnitsByCategoryInput {
5
+ categoryId: string;
6
+ }
7
+
8
+ export async function run(db: ReadonlyDB<DB>, input: ListUnitsByCategoryInput) {
9
+ const units = await db
10
+ .selectFrom("Unit")
11
+ .selectAll()
12
+ .where("categoryId", "=", input.categoryId)
13
+ .execute();
14
+
15
+ return { units };
16
+ }
@@ -2,10 +2,10 @@ import { defineConfig, defineGenerators } from "@tailor-platform/sdk";
2
2
 
3
3
  export default defineConfig({
4
4
  name: "primitives",
5
- db: { "main-db": { files: [`./src/db/*.ts`] } },
5
+ db: { "main-db": { files: [`./db/*.ts`] } },
6
6
  });
7
7
 
8
8
  export const generators = defineGenerators(
9
- ["@tailor-platform/kysely-type", { distPath: `./src/generated/kysely-tailordb.ts` }],
10
- ["@tailor-platform/enum-constants", { distPath: "./src/generated/enums.ts" }],
9
+ ["@tailor-platform/kysely-type", { distPath: `./generated/kysely-tailordb.ts` }],
10
+ ["@tailor-platform/enum-constants", { distPath: "./generated/enums.ts" }],
11
11
  );
@@ -11,32 +11,45 @@ describe("defineCommand", () => {
11
11
 
12
12
  it("calls impl with db and input when permission is present", async () => {
13
13
  const impl = vi.fn().mockResolvedValue({ result: "ok" });
14
- const command = defineCommand("mod:doThing", impl);
14
+ const command = defineCommand("mod:doThing", impl)();
15
15
 
16
16
  const result = await command("fake-db", { foo: "bar" }, ctx);
17
17
 
18
18
  expect(result).toEqual({ result: "ok" });
19
- expect(impl).toHaveBeenCalledWith("fake-db", { foo: "bar" });
19
+ expect(impl).toHaveBeenCalledWith("fake-db", { foo: "bar" }, ctx, undefined);
20
20
  });
21
21
 
22
- it("throws InsufficientPermissionError when permission is missing", async () => {
22
+ it("returns InsufficientPermissionError when permission is missing", async () => {
23
23
  const impl = vi.fn();
24
- const command = defineCommand("mod:doThing", impl);
24
+ const command = defineCommand("mod:doThing", impl)();
25
25
  const denied: CommandContext = { actorId: "user-1", permissions: [] };
26
26
 
27
- await expect(command("fake-db", {}, denied)).rejects.toBeInstanceOf(
28
- InsufficientPermissionError,
29
- );
27
+ const result = await command("fake-db", {}, denied);
28
+ expect(result.ok).toBe(false);
29
+ if (!result.ok) {
30
+ expect(result.error).toBeInstanceOf(InsufficientPermissionError);
31
+ }
30
32
  expect(impl).not.toHaveBeenCalled();
31
33
  });
32
34
 
33
- it("does not pass ctx to impl", async () => {
35
+ it("passes ctx to impl", async () => {
34
36
  const impl = vi.fn().mockResolvedValue({});
35
- const command = defineCommand("mod:doThing", impl);
37
+ const command = defineCommand("mod:doThing", impl)();
36
38
 
37
39
  await command("fake-db", { x: 1 }, ctx);
38
40
 
39
41
  expect(impl).toHaveBeenCalledTimes(1);
40
- expect(impl.mock.calls[0]).toHaveLength(2);
42
+ expect(impl.mock.calls[0]).toHaveLength(4);
43
+ expect(impl.mock.calls[0][2]).toEqual(ctx);
44
+ });
45
+
46
+ it("passes deps to impl when provided", async () => {
47
+ const impl = vi.fn().mockResolvedValue({ result: "ok" });
48
+ const deps = { maxDepth: 10 };
49
+ const command = defineCommand("mod:doThing", impl)(deps);
50
+
51
+ await command("fake-db", { x: 1 }, ctx);
52
+
53
+ expect(impl).toHaveBeenCalledWith("fake-db", { x: 1 }, ctx, deps);
41
54
  });
42
55
  });
@@ -1,19 +1,32 @@
1
+ import type { Result } from "./result";
1
2
  import type { CommandContext } from "./types";
2
3
  import { requirePermission } from "./requirePermission";
4
+ import type { InsufficientPermissionError } from "./errors";
3
5
 
4
- export type Command<TInput, TResult> = (
5
- db: unknown,
6
+ export type Command<TInput, TReturn extends Result<unknown, Error>> = (
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ db: any,
6
9
  input: TInput,
7
10
  ctx: CommandContext,
8
- ) => Promise<TResult>;
11
+ ) => Promise<TReturn>;
12
+
13
+ type CommandResult<TReturn extends Result<unknown, Error>> =
14
+ | TReturn
15
+ | Result<never, InstanceType<typeof InsufficientPermissionError>>;
9
16
 
10
- export function defineCommand<TInput, TResult>(
17
+ export function defineCommand<TDeps, TInput, TReturn extends Result<unknown, Error>>(
11
18
  permission: string,
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- impl: (db: any, input: TInput) => Promise<TResult>,
14
- ): Command<TInput, TResult> {
15
- return async (db, input, ctx) => {
16
- requirePermission(ctx, permission);
17
- return impl(db, input);
19
+ impl: (
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ db: any,
22
+ input: TInput,
23
+ ctx: CommandContext,
24
+ deps?: TDeps,
25
+ ) => Promise<TReturn>,
26
+ ): (deps?: TDeps) => Command<TInput, CommandResult<TReturn>> {
27
+ return (deps) => async (db, input, ctx) => {
28
+ const check = requirePermission(ctx, permission);
29
+ if (!check.ok) return check;
30
+ return impl(db, input, ctx, deps);
18
31
  };
19
32
  }
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { defineQuery } from "./defineQuery";
3
+ import type { QueryContext } from "./types";
4
+
5
+ describe("defineQuery", () => {
6
+ const ctx: QueryContext = { actorId: "user-1" };
7
+
8
+ it("calls impl with db, input, and ctx", async () => {
9
+ const impl = vi.fn().mockResolvedValue({ result: "ok" });
10
+ const query = defineQuery(impl);
11
+
12
+ const result = await query("fake-db", { foo: "bar" }, ctx);
13
+
14
+ expect(result).toEqual({ result: "ok" });
15
+ expect(impl).toHaveBeenCalledWith("fake-db", { foo: "bar" }, ctx);
16
+ });
17
+
18
+ it("passes ctx with actorId to impl", async () => {
19
+ const impl = vi.fn().mockResolvedValue({});
20
+ const query = defineQuery(impl);
21
+
22
+ await query("fake-db", { x: 1 }, ctx);
23
+
24
+ expect(impl).toHaveBeenCalledTimes(1);
25
+ expect(impl.mock.calls[0]).toHaveLength(3);
26
+ expect(impl.mock.calls[0][2]).toEqual({ actorId: "user-1" });
27
+ });
28
+ });
@@ -0,0 +1,16 @@
1
+ import type { QueryContext } from "./types";
2
+
3
+ export type Query<TInput, TResult> = (
4
+ db: unknown,
5
+ input: TInput,
6
+ ctx: QueryContext,
7
+ ) => Promise<TResult>;
8
+
9
+ export function defineQuery<TInput, TResult>(
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ impl: (db: any, input: TInput, ctx: QueryContext) => Promise<TResult>,
12
+ ): Query<TInput, TResult> {
13
+ return async (db, input, ctx) => {
14
+ return impl(db, input, ctx);
15
+ };
16
+ }
@@ -1,8 +1,10 @@
1
1
  export { defineCommand, type Command } from "./defineCommand";
2
+ export { ok, err, type Result, type ValueOf, type ErrorOf } from "./result";
3
+ export { defineQuery, type Query } from "./defineQuery";
2
4
  export { definePermissions } from "./definePermissions";
3
5
  export { requirePermission } from "./requirePermission";
4
6
  export { createDomainError, InsufficientPermissionError } from "./errors";
5
- export type { CommandContext } from "./types";
7
+ export type { CommandContext, QueryContext, ReadonlyDB } from "./types";
6
8
  export type {
7
9
  InferSchema,
8
10
  Selectable,
@@ -13,35 +13,36 @@ describe("requirePermission", () => {
13
13
  ],
14
14
  };
15
15
 
16
- it("does not throw when permission is present", () => {
17
- expect(() => requirePermission(ctx, "primitives:createCategory")).not.toThrow();
18
- expect(() => requirePermission(ctx, "inventory:createItem")).not.toThrow();
16
+ it("returns ok when permission is present", () => {
17
+ expect(requirePermission(ctx, "primitives:createCategory").ok).toBe(true);
18
+ expect(requirePermission(ctx, "inventory:createItem").ok).toBe(true);
19
19
  });
20
20
 
21
- it("throws InsufficientPermissionError when permission is missing", () => {
22
- expect(() => requirePermission(ctx, "primitives:deleteCategory")).toThrow(
23
- InsufficientPermissionError,
24
- );
21
+ it("returns InsufficientPermissionError when permission is missing", () => {
22
+ const result = requirePermission(ctx, "primitives:deleteCategory");
23
+ expect(result.ok).toBe(false);
24
+ if (!result.ok) {
25
+ expect(result.error).toBeInstanceOf(InsufficientPermissionError);
26
+ }
25
27
  });
26
28
 
27
29
  it("includes actorId and permission key in error", () => {
28
- try {
29
- requirePermission(ctx, "orders:createOrder");
30
- expect.fail("Should have thrown");
31
- } catch (err) {
32
- const error = err as InstanceType<typeof InsufficientPermissionError>;
33
- expect(error.name).toBe("InsufficientPermissionError");
34
- expect(error.code).toBe("INSUFFICIENT_PERMISSION");
35
- expect(error.message).toContain("user-1");
36
- expect(error.message).toContain("orders:createOrder");
30
+ const result = requirePermission(ctx, "orders:createOrder");
31
+ expect(result.ok).toBe(false);
32
+ if (!result.ok) {
33
+ expect(result.error.name).toBe("InsufficientPermissionError");
34
+ expect(result.error.code).toBe("INSUFFICIENT_PERMISSION");
35
+ expect(result.error.message).toContain("user-1");
36
+ expect(result.error.message).toContain("orders:createOrder");
37
37
  }
38
38
  });
39
39
 
40
- it("throws when permissions array is empty", () => {
40
+ it("returns error when permissions array is empty", () => {
41
41
  const emptyCtx: CommandContext = { actorId: "user-2", permissions: [] };
42
-
43
- expect(() => requirePermission(emptyCtx, "primitives:createCategory")).toThrow(
44
- InsufficientPermissionError,
45
- );
42
+ const result = requirePermission(emptyCtx, "primitives:createCategory");
43
+ expect(result.ok).toBe(false);
44
+ if (!result.ok) {
45
+ expect(result.error).toBeInstanceOf(InsufficientPermissionError);
46
+ }
46
47
  });
47
48
  });
@@ -1,8 +1,14 @@
1
1
  import type { CommandContext } from "./types";
2
+ import type { Result } from "./result";
2
3
  import { InsufficientPermissionError } from "./errors";
4
+ import { ok, err } from "./result";
3
5
 
4
- export function requirePermission(ctx: CommandContext, key: string): void {
6
+ export function requirePermission(
7
+ ctx: CommandContext,
8
+ key: string,
9
+ ): Result<void, InstanceType<typeof InsufficientPermissionError>> {
5
10
  if (!ctx.permissions.includes(key)) {
6
- throw new InsufficientPermissionError(ctx.actorId, key);
11
+ return err(new InsufficientPermissionError(ctx.actorId, key));
7
12
  }
13
+ return ok(undefined);
8
14
  }
@@ -0,0 +1,12 @@
1
+ export type Result<T, E extends Error = never> = { ok: true; value: T } | { ok: false; error: E };
2
+
3
+ export type ValueOf<R> = R extends { ok: true; value: infer T } ? T : never;
4
+ export type ErrorOf<R> = R extends { ok: false; error: infer E } ? E : never;
5
+
6
+ export function ok<T>(value: T): { ok: true; value: T } {
7
+ return { ok: true, value };
8
+ }
9
+
10
+ export function err<E extends Error>(error: E): { ok: false; error: E } {
11
+ return { ok: false, error };
12
+ }
@@ -1,4 +1,12 @@
1
+ export interface QueryContext {
2
+ actorId: string;
3
+ }
4
+
1
5
  export interface CommandContext {
2
6
  actorId: string;
3
7
  permissions: readonly string[];
4
8
  }
9
+
10
+ // Strips write methods (updateTable, insertInto, deleteFrom) from a Kysely DB instance.
11
+ // Query handlers use this instead of the full DB type to get compile-time write prevention.
12
+ export type ReadonlyDB<T extends { selectFrom: unknown }> = Pick<T, "selectFrom">;