@tailor-platform/erp-kit 0.1.2 → 0.2.1

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 (330) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +80 -12
  3. package/dist/cli.js +1070 -450
  4. package/package.json +11 -8
  5. package/schemas/app-compose/business-flow.yml +3 -0
  6. package/schemas/app-compose/story.yml +1 -1
  7. package/schemas/module/model.yml +5 -0
  8. package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +8 -14
  9. package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +6 -13
  10. package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -6
  11. package/skills/{app-compose-6-implementation-spec → erp-kit-app-4-impl-spec}/SKILL.md +11 -22
  12. package/skills/erp-kit-app-5-implementation/SKILL.md +149 -0
  13. package/skills/erp-kit-app-5-implementation/references/backend.md +232 -0
  14. package/skills/erp-kit-app-5-implementation/references/frontend.md +242 -0
  15. package/skills/{mock-scenario → erp-kit-mock-scenario}/SKILL.md +1 -1
  16. package/skills/{1-module-docs → erp-kit-module-1-docs}/SKILL.md +2 -2
  17. package/skills/{2-module-feature-breakdown → erp-kit-module-2-feature-breakdown}/SKILL.md +13 -9
  18. package/skills/erp-kit-module-2-feature-breakdown/references/naming.md +59 -0
  19. package/skills/{3-module-doc-review → erp-kit-module-3-doc-review}/SKILL.md +83 -25
  20. package/skills/erp-kit-module-4-tdd/SKILL.md +94 -0
  21. package/skills/erp-kit-module-4-tdd/references/cross-module-dependency.md +133 -0
  22. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/db-relations.md +5 -1
  23. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/exports.md +1 -1
  24. package/skills/erp-kit-module-4-tdd/references/generated-code.md +32 -0
  25. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/SKILL.md +46 -44
  26. package/skills/erp-kit-module-5-impl-review/references/commands.md +62 -0
  27. package/skills/erp-kit-module-5-impl-review/references/errors.md +10 -0
  28. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/references/testing.md +1 -1
  29. package/skills/erp-kit-module-shared/SKILL.md +16 -0
  30. package/skills/erp-kit-module-shared/references/commands.md +203 -0
  31. package/skills/erp-kit-module-shared/references/errors.md +35 -0
  32. package/skills/erp-kit-module-shared/references/queries.md +168 -0
  33. package/skills/erp-kit-module-shared/references/structure.md +36 -0
  34. package/skills/{3-module-doc-review → erp-kit-module-shared}/references/testing.md +4 -3
  35. package/skills/erp-kit-update/SKILL.md +64 -0
  36. package/src/cli.doc.test.ts +65 -0
  37. package/src/cli.ts +3 -35
  38. package/src/commands/app/index.ts +3 -3
  39. package/src/commands/check.test.ts +1 -1
  40. package/src/commands/check.ts +2 -2
  41. package/src/commands/index.ts +73 -0
  42. package/src/commands/init.test.ts +22 -5
  43. package/src/commands/init.ts +25 -16
  44. package/src/commands/license.ts +193 -0
  45. package/src/commands/mock/index.ts +2 -2
  46. package/src/commands/mock/start.ts +1 -1
  47. package/src/commands/mock/validate.test.ts +1 -1
  48. package/src/commands/module/generate.ts +35 -0
  49. package/src/commands/module/index.ts +6 -4
  50. package/src/commands/module/list.test.ts +7 -12
  51. package/src/commands/module/list.ts +1 -1
  52. package/src/commands/scaffold-templates.ts +65 -0
  53. package/src/commands/scaffold.test.ts +92 -2
  54. package/src/commands/scaffold.ts +22 -2
  55. package/src/commands/sync-check.test.ts +60 -1
  56. package/src/commands/sync-check.ts +35 -2
  57. package/src/generator/generate-code.test.ts +200 -0
  58. package/src/generator/generate-code.ts +260 -0
  59. package/src/generator/parse-command-doc.test.ts +159 -0
  60. package/src/generator/parse-command-doc.ts +116 -0
  61. package/src/integration.test.ts +2 -2
  62. package/src/module.ts +44 -6
  63. package/src/modules/item-management/README.md +38 -0
  64. package/src/modules/item-management/command/activateItem.generated.ts +6 -0
  65. package/src/modules/item-management/command/activateItem.test.ts +76 -0
  66. package/src/modules/item-management/command/activateItem.ts +42 -0
  67. package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +6 -0
  68. package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +88 -0
  69. package/src/modules/item-management/command/assignItemToTaxonomy.ts +63 -0
  70. package/src/modules/item-management/command/createItem.generated.ts +6 -0
  71. package/src/modules/item-management/command/createItem.test.ts +152 -0
  72. package/src/modules/item-management/command/createItem.ts +72 -0
  73. package/src/modules/item-management/command/createTaxonomyNode.generated.ts +6 -0
  74. package/src/modules/item-management/command/createTaxonomyNode.test.ts +126 -0
  75. package/src/modules/item-management/command/createTaxonomyNode.ts +70 -0
  76. package/src/modules/item-management/command/deactivateItem.generated.ts +6 -0
  77. package/src/modules/item-management/command/deactivateItem.test.ts +76 -0
  78. package/src/modules/item-management/command/deactivateItem.ts +42 -0
  79. package/src/modules/item-management/command/deleteItem.generated.ts +6 -0
  80. package/src/modules/item-management/command/deleteItem.test.ts +61 -0
  81. package/src/modules/item-management/command/deleteItem.ts +38 -0
  82. package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +6 -0
  83. package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +73 -0
  84. package/src/modules/item-management/command/deleteTaxonomyNode.ts +50 -0
  85. package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +6 -0
  86. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +136 -0
  87. package/src/modules/item-management/command/moveTaxonomyNode.ts +85 -0
  88. package/src/modules/item-management/command/reactivateItem.generated.ts +6 -0
  89. package/src/modules/item-management/command/reactivateItem.test.ts +76 -0
  90. package/src/modules/item-management/command/reactivateItem.ts +42 -0
  91. package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +6 -0
  92. package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +43 -0
  93. package/src/modules/item-management/command/removeItemFromTaxonomy.ts +30 -0
  94. package/src/modules/item-management/command/updateItem.generated.ts +6 -0
  95. package/src/modules/item-management/command/updateItem.test.ts +178 -0
  96. package/src/modules/item-management/command/updateItem.ts +103 -0
  97. package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +6 -0
  98. package/src/modules/item-management/command/updateTaxonomyNode.test.ts +88 -0
  99. package/src/modules/item-management/command/updateTaxonomyNode.ts +62 -0
  100. package/src/modules/item-management/db/item.ts +47 -0
  101. package/src/modules/item-management/db/itemTaxonomyAssignment.ts +49 -0
  102. package/src/modules/item-management/db/taxonomyNode.ts +34 -0
  103. package/src/modules/item-management/docs/commands/ActivateItem.md +32 -0
  104. package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +38 -0
  105. package/src/modules/item-management/docs/commands/CreateItem.md +44 -0
  106. package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +44 -0
  107. package/src/modules/item-management/docs/commands/DeactivateItem.md +34 -0
  108. package/src/modules/item-management/docs/commands/DeleteItem.md +35 -0
  109. package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +39 -0
  110. package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +45 -0
  111. package/src/modules/item-management/docs/commands/ReactivateItem.md +34 -0
  112. package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +30 -0
  113. package/src/modules/item-management/docs/commands/UpdateItem.md +55 -0
  114. package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +36 -0
  115. package/src/modules/item-management/docs/features/item-lifecycle.md +60 -0
  116. package/src/modules/item-management/docs/features/item-taxonomy.md +65 -0
  117. package/src/modules/item-management/docs/models/ItemTaxonomyAssignment.md +36 -0
  118. package/src/modules/item-management/docs/models/TaxonomyNode.md +47 -0
  119. package/src/modules/item-management/docs/models/item.md +59 -0
  120. package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +36 -0
  121. package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +40 -0
  122. package/src/modules/item-management/docs/queries/DetectCircularReference.md +41 -0
  123. package/src/modules/item-management/docs/queries/GetItem.md +38 -0
  124. package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +29 -0
  125. package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +35 -0
  126. package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +29 -0
  127. package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +29 -0
  128. package/src/modules/item-management/generated/enums.ts +9 -0
  129. package/src/modules/item-management/generated/kysely-tailordb.ts +62 -0
  130. package/src/modules/item-management/index.ts +53 -0
  131. package/src/modules/item-management/lib/_db_deps.ts +13 -0
  132. package/src/modules/item-management/lib/errors.generated.ts +117 -0
  133. package/src/modules/item-management/lib/permissions.generated.ts +17 -0
  134. package/src/modules/item-management/lib/types.ts +19 -0
  135. package/src/modules/item-management/module.ts +97 -0
  136. package/src/modules/item-management/query/calculateNodeDepth.generated.ts +5 -0
  137. package/src/modules/item-management/query/calculateNodeDepth.test.ts +56 -0
  138. package/src/modules/item-management/query/calculateNodeDepth.ts +28 -0
  139. package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +5 -0
  140. package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +75 -0
  141. package/src/modules/item-management/query/calculateSubtreeDepth.ts +29 -0
  142. package/src/modules/item-management/query/detectCircularReference.generated.ts +5 -0
  143. package/src/modules/item-management/query/detectCircularReference.test.ts +61 -0
  144. package/src/modules/item-management/query/detectCircularReference.ts +32 -0
  145. package/src/modules/item-management/query/getItem.generated.ts +5 -0
  146. package/src/modules/item-management/query/getItem.test.ts +67 -0
  147. package/src/modules/item-management/query/getItem.ts +20 -0
  148. package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +5 -0
  149. package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +25 -0
  150. package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +18 -0
  151. package/src/modules/item-management/query/getTaxonomyNode.generated.ts +5 -0
  152. package/src/modules/item-management/query/getTaxonomyNode.test.ts +47 -0
  153. package/src/modules/item-management/query/getTaxonomyNode.ts +18 -0
  154. package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +5 -0
  155. package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +25 -0
  156. package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +16 -0
  157. package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +5 -0
  158. package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +34 -0
  159. package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +16 -0
  160. package/src/modules/item-management/tailor.config.ts +11 -0
  161. package/src/modules/item-management/testing/fixtures.ts +81 -0
  162. package/src/modules/primitives/command/activateCategory.generated.ts +6 -0
  163. package/src/modules/primitives/command/activateCategory.test.ts +11 -29
  164. package/src/modules/primitives/command/activateCategory.ts +27 -34
  165. package/src/modules/primitives/command/activateCurrency.generated.ts +6 -0
  166. package/src/modules/primitives/command/activateCurrency.test.ts +11 -29
  167. package/src/modules/primitives/command/activateCurrency.ts +27 -34
  168. package/src/modules/primitives/command/activateUnit.generated.ts +6 -0
  169. package/src/modules/primitives/command/activateUnit.test.ts +11 -15
  170. package/src/modules/primitives/command/activateUnit.ts +27 -34
  171. package/src/modules/primitives/command/createCategory.generated.ts +6 -0
  172. package/src/modules/primitives/command/createCategory.test.ts +27 -39
  173. package/src/modules/primitives/command/createCategory.ts +53 -62
  174. package/src/modules/primitives/command/createCurrency.generated.ts +6 -0
  175. package/src/modules/primitives/command/createCurrency.test.ts +78 -71
  176. package/src/modules/primitives/command/createCurrency.ts +43 -48
  177. package/src/modules/primitives/command/createExchangeRate.generated.ts +6 -0
  178. package/src/modules/primitives/command/createExchangeRate.test.ts +101 -100
  179. package/src/modules/primitives/command/createExchangeRate.ts +50 -59
  180. package/src/modules/primitives/command/createUnit.generated.ts +6 -0
  181. package/src/modules/primitives/command/createUnit.test.ts +92 -95
  182. package/src/modules/primitives/command/createUnit.ts +54 -57
  183. package/src/modules/primitives/command/deactivateCategory.generated.ts +6 -0
  184. package/src/modules/primitives/command/deactivateCategory.test.ts +27 -28
  185. package/src/modules/primitives/command/deactivateCategory.ts +43 -50
  186. package/src/modules/primitives/command/deactivateCurrency.generated.ts +6 -0
  187. package/src/modules/primitives/command/deactivateCurrency.test.ts +23 -38
  188. package/src/modules/primitives/command/deactivateCurrency.ts +31 -38
  189. package/src/modules/primitives/command/deactivateUnit.generated.ts +6 -0
  190. package/src/modules/primitives/command/deactivateUnit.test.ts +27 -23
  191. package/src/modules/primitives/command/deactivateUnit.ts +39 -49
  192. package/src/modules/primitives/command/setBaseCurrency.generated.ts +6 -0
  193. package/src/modules/primitives/command/setBaseCurrency.test.ts +40 -33
  194. package/src/modules/primitives/command/setBaseCurrency.ts +43 -50
  195. package/src/modules/primitives/command/setReferenceUnit.generated.ts +6 -0
  196. package/src/modules/primitives/command/setReferenceUnit.test.ts +39 -35
  197. package/src/modules/primitives/command/setReferenceUnit.ts +46 -59
  198. package/src/modules/primitives/db/unit.ts +13 -3
  199. package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -2
  200. package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -2
  201. package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -2
  202. package/src/modules/primitives/docs/commands/CreateCategory.md +1 -4
  203. package/src/modules/primitives/docs/commands/CreateCurrency.md +3 -4
  204. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +4 -5
  205. package/src/modules/primitives/docs/commands/CreateUnit.md +5 -5
  206. package/src/modules/primitives/docs/commands/DeactivateCategory.md +2 -3
  207. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +2 -3
  208. package/src/modules/primitives/docs/commands/DeactivateUnit.md +2 -3
  209. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +2 -3
  210. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +2 -3
  211. package/src/modules/primitives/docs/queries/ConvertAmount.md +3 -5
  212. package/src/modules/primitives/docs/queries/ConvertQuantity.md +3 -5
  213. package/src/modules/primitives/docs/queries/GetBaseCurrency.md +32 -0
  214. package/src/modules/primitives/docs/queries/GetCurrency.md +36 -0
  215. package/src/modules/primitives/docs/queries/GetUnit.md +36 -0
  216. package/src/modules/primitives/docs/queries/GetUoMCategory.md +36 -0
  217. package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +26 -0
  218. package/src/modules/primitives/generated/kysely-tailordb.ts +24 -45
  219. package/src/modules/primitives/index.ts +15 -4
  220. package/src/modules/primitives/lib/errors.generated.ts +112 -0
  221. package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -8
  222. package/src/modules/primitives/module.ts +37 -27
  223. package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
  224. package/src/modules/primitives/query/convertAmount.test.ts +2 -2
  225. package/src/modules/primitives/query/convertAmount.ts +27 -28
  226. package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
  227. package/src/modules/primitives/query/convertQuantity.test.ts +6 -2
  228. package/src/modules/primitives/query/convertQuantity.ts +49 -57
  229. package/src/modules/primitives/query/getBaseCurrency.generated.ts +5 -0
  230. package/src/modules/primitives/query/getBaseCurrency.test.ts +28 -0
  231. package/src/modules/primitives/query/getBaseCurrency.ts +16 -0
  232. package/src/modules/primitives/query/getCurrency.generated.ts +5 -0
  233. package/src/modules/primitives/query/getCurrency.test.ts +47 -0
  234. package/src/modules/primitives/query/getCurrency.ts +18 -0
  235. package/src/modules/primitives/query/getUnit.generated.ts +5 -0
  236. package/src/modules/primitives/query/getUnit.test.ts +47 -0
  237. package/src/modules/primitives/query/getUnit.ts +18 -0
  238. package/src/modules/primitives/query/getUoMCategory.generated.ts +5 -0
  239. package/src/modules/primitives/query/getUoMCategory.test.ts +47 -0
  240. package/src/modules/primitives/query/getUoMCategory.ts +18 -0
  241. package/src/modules/primitives/query/listUnitsByCategory.generated.ts +5 -0
  242. package/src/modules/primitives/query/listUnitsByCategory.ts +16 -0
  243. package/src/modules/primitives/tailor.config.ts +3 -3
  244. package/src/modules/shared/defineCommand.test.ts +23 -10
  245. package/src/modules/shared/defineCommand.ts +23 -10
  246. package/src/modules/shared/internal.ts +1 -0
  247. package/src/modules/shared/requirePermission.test.ts +22 -21
  248. package/src/modules/shared/requirePermission.ts +8 -2
  249. package/src/modules/shared/result.ts +12 -0
  250. package/src/modules/testing/index.ts +36 -11
  251. package/src/modules/user-management/command/activateUser.generated.ts +6 -0
  252. package/src/modules/user-management/command/activateUser.test.ts +27 -27
  253. package/src/modules/user-management/command/activateUser.ts +40 -48
  254. package/src/modules/user-management/command/assignPermissionToRole.generated.ts +6 -0
  255. package/src/modules/user-management/command/assignPermissionToRole.test.ts +42 -43
  256. package/src/modules/user-management/command/assignPermissionToRole.ts +59 -62
  257. package/src/modules/user-management/command/assignRoleToUser.generated.ts +6 -0
  258. package/src/modules/user-management/command/assignRoleToUser.test.ts +70 -63
  259. package/src/modules/user-management/command/assignRoleToUser.ts +63 -66
  260. package/src/modules/user-management/command/createPermission.generated.ts +6 -0
  261. package/src/modules/user-management/command/createPermission.test.ts +45 -38
  262. package/src/modules/user-management/command/createPermission.ts +42 -46
  263. package/src/modules/user-management/command/createRole.generated.ts +6 -0
  264. package/src/modules/user-management/command/createRole.test.ts +30 -29
  265. package/src/modules/user-management/command/createRole.ts +33 -33
  266. package/src/modules/user-management/command/createUser.generated.ts +6 -0
  267. package/src/modules/user-management/command/createUser.test.ts +64 -42
  268. package/src/modules/user-management/command/createUser.ts +54 -56
  269. package/src/modules/user-management/command/deactivateUser.generated.ts +6 -0
  270. package/src/modules/user-management/command/deactivateUser.test.ts +27 -27
  271. package/src/modules/user-management/command/deactivateUser.ts +40 -48
  272. package/src/modules/user-management/command/logAuditEvent.generated.ts +6 -0
  273. package/src/modules/user-management/command/logAuditEvent.test.ts +50 -42
  274. package/src/modules/user-management/command/logAuditEvent.ts +25 -28
  275. package/src/modules/user-management/command/reactivateUser.generated.ts +6 -0
  276. package/src/modules/user-management/command/reactivateUser.test.ts +31 -27
  277. package/src/modules/user-management/command/reactivateUser.ts +40 -48
  278. package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +6 -0
  279. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +52 -51
  280. package/src/modules/user-management/command/revokePermissionFromRole.ts +60 -57
  281. package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +6 -0
  282. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +53 -48
  283. package/src/modules/user-management/command/revokeRoleFromUser.ts +58 -57
  284. package/src/modules/user-management/docs/commands/CreatePermission.md +2 -2
  285. package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
  286. package/src/modules/user-management/generated/enums.ts +11 -11
  287. package/src/modules/user-management/generated/kysely-tailordb.ts +27 -56
  288. package/src/modules/user-management/index.ts +2 -2
  289. package/src/modules/user-management/lib/errors.generated.ts +67 -0
  290. package/src/modules/user-management/{permissions.ts → lib/permissions.generated.ts} +8 -7
  291. package/src/modules/user-management/module.ts +22 -22
  292. package/src/modules/user-management/tailor.config.ts +3 -3
  293. package/src/schemas.ts +1 -1
  294. package/skills/1-module-docs/references/structure.md +0 -22
  295. package/skills/2-module-feature-breakdown/references/commands.md +0 -48
  296. package/skills/2-module-feature-breakdown/references/structure.md +0 -22
  297. package/skills/3-module-doc-review/references/commands.md +0 -54
  298. package/skills/3-module-doc-review/references/models.md +0 -29
  299. package/skills/4-module-tdd-implementation/SKILL.md +0 -74
  300. package/skills/4-module-tdd-implementation/references/commands.md +0 -45
  301. package/skills/4-module-tdd-implementation/references/errors.md +0 -7
  302. package/skills/4-module-tdd-implementation/references/models.md +0 -30
  303. package/skills/4-module-tdd-implementation/references/structure.md +0 -22
  304. package/skills/4-module-tdd-implementation/references/testing.md +0 -37
  305. package/skills/5-module-implementation-review/references/commands.md +0 -45
  306. package/skills/5-module-implementation-review/references/errors.md +0 -7
  307. package/skills/5-module-implementation-review/references/exports.md +0 -8
  308. package/skills/5-module-implementation-review/references/models.md +0 -30
  309. package/skills/app-compose-1-requirement-analysis/references/structure.md +0 -27
  310. package/skills/app-compose-2-requirements-breakdown/references/screen-detailview.md +0 -106
  311. package/skills/app-compose-2-requirements-breakdown/references/screen-form.md +0 -139
  312. package/skills/app-compose-2-requirements-breakdown/references/screen-listview.md +0 -153
  313. package/skills/app-compose-2-requirements-breakdown/references/structure.md +0 -27
  314. package/skills/app-compose-3-doc-review/references/structure.md +0 -27
  315. package/skills/app-compose-4-design-mock/SKILL.md +0 -256
  316. package/skills/app-compose-4-design-mock/references/component.md +0 -50
  317. package/skills/app-compose-4-design-mock/references/screen-detailview.md +0 -106
  318. package/skills/app-compose-4-design-mock/references/screen-form.md +0 -139
  319. package/skills/app-compose-4-design-mock/references/screen-listview.md +0 -153
  320. package/skills/app-compose-4-design-mock/references/structure.md +0 -27
  321. package/skills/app-compose-5-design-mock-review/SKILL.md +0 -290
  322. package/skills/app-compose-5-design-mock-review/references/component.md +0 -50
  323. package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +0 -106
  324. package/skills/app-compose-5-design-mock-review/references/screen-form.md +0 -139
  325. package/skills/app-compose-5-design-mock-review/references/screen-listview.md +0 -153
  326. package/skills/app-compose-6-implementation-spec/references/auth.md +0 -72
  327. package/skills/app-compose-6-implementation-spec/references/structure.md +0 -27
  328. package/src/modules/primitives/lib/errors.ts +0 -138
  329. package/src/modules/user-management/lib/errors.ts +0 -81
  330. /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
@@ -1,12 +1,10 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { InsufficientPermissionError, type CommandContext } from "../../shared/internal";
2
+ import { type CommandContext } from "../../shared/internal";
3
3
  import { createMockDb } from "../../testing/index";
4
4
  import { DB } from "../generated/kysely-tailordb";
5
- import { DuplicateRoleNameError, MissingRequiredFieldError } from "../lib/errors";
5
+ import { DuplicateRoleNameError, MissingRequiredFieldError } from "../lib/errors.generated";
6
6
  import { baseRole } from "../testing/fixtures";
7
- import { makeCreateRole } from "./createRole";
8
-
9
- const createRole = makeCreateRole();
7
+ import { run } from "./createRole";
10
8
 
11
9
  describe("createRole", () => {
12
10
  const ctx: CommandContext = {
@@ -18,26 +16,32 @@ describe("createRole", () => {
18
16
  it("throws when name is empty", async () => {
19
17
  const { db } = createMockDb<DB>();
20
18
 
21
- await expect(createRole(db, { name: "" }, ctx)).rejects.toBeInstanceOf(
22
- MissingRequiredFieldError,
23
- );
19
+ const result = await run(db, { name: "" }, ctx);
20
+ expect(result.ok).toBe(false);
21
+ if (!result.ok) {
22
+ expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
23
+ }
24
24
  });
25
25
 
26
26
  it("throws when name is whitespace only", async () => {
27
27
  const { db } = createMockDb<DB>();
28
28
 
29
- await expect(createRole(db, { name: " " }, ctx)).rejects.toBeInstanceOf(
30
- MissingRequiredFieldError,
31
- );
29
+ const result = await run(db, { name: " " }, ctx);
30
+ expect(result.ok).toBe(false);
31
+ if (!result.ok) {
32
+ expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
33
+ }
32
34
  });
33
35
 
34
36
  it("throws when role name already exists", async () => {
35
37
  const { db, spies } = createMockDb<DB>();
36
38
  spies.select.mockReturnValue(baseRole);
37
39
 
38
- await expect(createRole(db, { name: baseRole.name }, ctx)).rejects.toBeInstanceOf(
39
- DuplicateRoleNameError,
40
- );
40
+ const result = await run(db, { name: baseRole.name }, ctx);
41
+ expect(result.ok).toBe(false);
42
+ if (!result.ok) {
43
+ expect(result.error).toBeInstanceOf(DuplicateRoleNameError);
44
+ }
41
45
  });
42
46
 
43
47
  // Success cases
@@ -53,9 +57,12 @@ describe("createRole", () => {
53
57
  spies.select.mockReturnValue(undefined); // No existing role
54
58
  spies.insert.mockReturnValue(createdRole);
55
59
 
56
- const result = await createRole(db, { name: "Viewer" }, ctx);
60
+ const result = await run(db, { name: "Viewer" }, ctx);
57
61
 
58
- expect(result.role.name).toBe("Viewer");
62
+ expect(result.ok).toBe(true);
63
+ if (result.ok) {
64
+ expect(result.value.role.name).toBe("Viewer");
65
+ }
59
66
  expect(spies.insert).toHaveBeenCalled();
60
67
  });
61
68
 
@@ -71,7 +78,7 @@ describe("createRole", () => {
71
78
  spies.select.mockReturnValue(undefined);
72
79
  spies.insert.mockReturnValue(createdRole);
73
80
 
74
- const result = await createRole(
81
+ const result = await run(
75
82
  db,
76
83
  {
77
84
  name: "Editor",
@@ -80,20 +87,14 @@ describe("createRole", () => {
80
87
  ctx,
81
88
  );
82
89
 
83
- expect(result.role.name).toBe("Editor");
84
- expect(result.role.description).toBe("Can edit content");
85
- });
86
-
87
- it("throws when permission is missing", async () => {
88
- const { db } = createMockDb<DB>();
89
- const denied: CommandContext = { actorId: "test-actor", permissions: [] };
90
- await expect(createRole(db, { name: "Test" }, denied)).rejects.toBeInstanceOf(
91
- InsufficientPermissionError,
92
- );
90
+ expect(result.ok).toBe(true);
91
+ if (result.ok) {
92
+ expect(result.value.role.name).toBe("Editor");
93
+ expect(result.value.role.description).toBe("Can edit content");
94
+ }
93
95
  });
94
96
 
95
97
  it("passes custom fields through to insert", async () => {
96
- const createRoleWithFields = makeCreateRole<{ department: string }>();
97
98
  const { db, spies } = createMockDb<DB>();
98
99
  const createdRole = {
99
100
  ...baseRole,
@@ -105,7 +106,7 @@ describe("createRole", () => {
105
106
  spies.select.mockReturnValue(undefined);
106
107
  spies.insert.mockReturnValue(createdRole);
107
108
 
108
- await createRoleWithFields(db, { name: "Viewer", department: "Engineering" }, ctx);
109
+ await run(db, { name: "Viewer", department: "Engineering" }, ctx);
109
110
 
110
111
  expect(spies.values).toHaveBeenNthCalledWith(
111
112
  1,
@@ -1,7 +1,6 @@
1
- import { defineCommand } from "../../shared/internal";
1
+ import { ok, err, type CommandContext } from "../../shared/internal";
2
2
  import { DB } from "../generated/kysely-tailordb";
3
- import { DuplicateRoleNameError, MissingRequiredFieldError } from "../lib/errors";
4
- import { permissions } from "../permissions";
3
+ import { DuplicateRoleNameError, MissingRequiredFieldError } from "../lib/errors.generated";
5
4
 
6
5
  interface CreateRoleInput {
7
6
  name: string;
@@ -14,39 +13,40 @@ interface CreateRoleInput {
14
13
  * Creates a new role with the specified name.
15
14
  * Validates that the name is unique within the system.
16
15
  */
17
- export function makeCreateRole<CF extends Record<string, unknown>>() {
18
- return defineCommand(permissions.createRole, async (db: DB, input: CreateRoleInput & CF) => {
19
- const { name, description, ...customFields } = input;
16
+ export async function run<CF extends Record<string, unknown>>(
17
+ db: DB,
18
+ input: CreateRoleInput & CF,
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ _ctx: CommandContext,
21
+ ) {
22
+ const { name, description, ...customFields } = input;
20
23
 
21
- // 1. Validate name is provided
22
- if (!name || name.trim() === "") {
23
- throw new MissingRequiredFieldError("name");
24
- }
24
+ if (!name || name.trim() === "") {
25
+ return err(new MissingRequiredFieldError("name"));
26
+ }
25
27
 
26
- // 2. Check if role name already exists
27
- const existingRole = await db
28
- .selectFrom("Role")
29
- .selectAll()
30
- .where("name", "=", name)
31
- .executeTakeFirst();
28
+ const existingRole = await db
29
+ .selectFrom("Role")
30
+ .selectAll()
31
+ .where("name", "=", name)
32
+ .executeTakeFirst();
32
33
 
33
- if (existingRole) {
34
- throw new DuplicateRoleNameError(name);
35
- }
34
+ if (existingRole) {
35
+ return err(new DuplicateRoleNameError(name));
36
+ }
36
37
 
37
- // 3. Create role
38
- const role = await db
39
- .insertInto("Role")
40
- .values({
41
- ...(customFields as Record<string, unknown>),
42
- name,
43
- description: description ?? null,
44
- createdAt: new Date(),
45
- updatedAt: null,
46
- })
47
- .returningAll()
48
- .executeTakeFirst();
38
+ // 3. Create role
39
+ const role = await db
40
+ .insertInto("Role")
41
+ .values({
42
+ ...(customFields as Record<string, unknown>),
43
+ name,
44
+ description: description ?? null,
45
+ createdAt: new Date(),
46
+ updatedAt: null,
47
+ })
48
+ .returningAll()
49
+ .executeTakeFirst();
49
50
 
50
- return { role: role! };
51
- });
51
+ return ok({ role: role! });
52
52
  }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { defineCommand } from "../../shared/internal";
3
+ import { permissions } from "../lib/permissions.generated";
4
+ import { run } from "./createUser";
5
+
6
+ export const createUser = defineCommand(permissions.createUser, run);
@@ -1,16 +1,14 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { InsufficientPermissionError, type CommandContext } from "../../shared/internal";
2
+ import { type CommandContext } from "../../shared/internal";
3
3
  import { createMockDb } from "../../testing/index";
4
4
  import { DB } from "../generated/kysely-tailordb";
5
5
  import {
6
6
  InvalidEmailError,
7
7
  MissingRequiredFieldError,
8
8
  UserAlreadyExistsError,
9
- } from "../lib/errors";
9
+ } from "../lib/errors.generated";
10
10
  import { activeUser, baseAuditEvent, pendingUser } from "../testing/fixtures";
11
- import { makeCreateUser } from "./createUser";
12
-
13
- const createUser = makeCreateUser();
11
+ import { run } from "./createUser";
14
12
 
15
13
  describe("createUser", () => {
16
14
  const ctx: CommandContext = {
@@ -22,50 +20,74 @@ describe("createUser", () => {
22
20
  it("throws when email is empty", async () => {
23
21
  const { db } = createMockDb<DB>();
24
22
 
25
- await expect(
26
- createUser(db, { email: "", name: "Test User", actorId: "actor-1" }, ctx),
27
- ).rejects.toBeInstanceOf(MissingRequiredFieldError);
23
+ const result = await run(db, { email: "", name: "Test User", actorId: "actor-1" }, ctx);
24
+ expect(result.ok).toBe(false);
25
+ if (!result.ok) {
26
+ expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
27
+ }
28
28
  });
29
29
 
30
30
  it("throws when name is empty", async () => {
31
31
  const { db } = createMockDb<DB>();
32
32
 
33
- await expect(
34
- createUser(db, { email: "test@example.com", name: "", actorId: "actor-1" }, ctx),
35
- ).rejects.toBeInstanceOf(MissingRequiredFieldError);
33
+ const result = await run(db, { email: "test@example.com", name: "", actorId: "actor-1" }, ctx);
34
+ expect(result.ok).toBe(false);
35
+ if (!result.ok) {
36
+ expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
37
+ }
36
38
  });
37
39
 
38
40
  it("throws when name is whitespace only", async () => {
39
41
  const { db } = createMockDb<DB>();
40
42
 
41
- await expect(
42
- createUser(db, { email: "test@example.com", name: " ", actorId: "actor-1" }, ctx),
43
- ).rejects.toBeInstanceOf(MissingRequiredFieldError);
43
+ const result = await run(
44
+ db,
45
+ { email: "test@example.com", name: " ", actorId: "actor-1" },
46
+ ctx,
47
+ );
48
+ expect(result.ok).toBe(false);
49
+ if (!result.ok) {
50
+ expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
51
+ }
44
52
  });
45
53
 
46
54
  it("throws when email format is invalid (no @)", async () => {
47
55
  const { db } = createMockDb<DB>();
48
56
 
49
- await expect(
50
- createUser(db, { email: "invalidemail", name: "Test User", actorId: "actor-1" }, ctx),
51
- ).rejects.toBeInstanceOf(InvalidEmailError);
57
+ const result = await run(
58
+ db,
59
+ { email: "invalidemail", name: "Test User", actorId: "actor-1" },
60
+ ctx,
61
+ );
62
+ expect(result.ok).toBe(false);
63
+ if (!result.ok) {
64
+ expect(result.error).toBeInstanceOf(InvalidEmailError);
65
+ }
52
66
  });
53
67
 
54
68
  it("throws when email format is invalid (no domain)", async () => {
55
69
  const { db } = createMockDb<DB>();
56
70
 
57
- await expect(
58
- createUser(db, { email: "test@", name: "Test User", actorId: "actor-1" }, ctx),
59
- ).rejects.toBeInstanceOf(InvalidEmailError);
71
+ const result = await run(db, { email: "test@", name: "Test User", actorId: "actor-1" }, ctx);
72
+ expect(result.ok).toBe(false);
73
+ if (!result.ok) {
74
+ expect(result.error).toBeInstanceOf(InvalidEmailError);
75
+ }
60
76
  });
61
77
 
62
78
  it("throws when email already exists", async () => {
63
79
  const { db, spies } = createMockDb<DB>();
64
80
  spies.select.mockReturnValue(activeUser);
65
81
 
66
- await expect(
67
- createUser(db, { email: activeUser.email, name: "New User", actorId: "actor-1" }, ctx),
68
- ).rejects.toBeInstanceOf(UserAlreadyExistsError);
82
+ const result = await run(
83
+ db,
84
+ { email: activeUser.email, name: "New User", actorId: "actor-1" },
85
+ ctx,
86
+ );
87
+ expect(result.ok).toBe(false);
88
+ if (!result.ok) {
89
+ expect(result.error).toBeInstanceOf(UserAlreadyExistsError);
90
+ }
69
91
  });
70
92
 
71
93
  // Success cases
@@ -89,7 +111,7 @@ describe("createUser", () => {
89
111
  spies.select.mockReturnValue(undefined); // No existing user
90
112
  spies.insert.mockReturnValueOnce(createdUser).mockReturnValueOnce(createdAuditEvent);
91
113
 
92
- const result = await createUser(
114
+ const result = await run(
93
115
  db,
94
116
  {
95
117
  email: "new@example.com",
@@ -99,9 +121,12 @@ describe("createUser", () => {
99
121
  ctx,
100
122
  );
101
123
 
102
- expect(result.user.email).toBe("new@example.com");
103
- expect(result.user.name).toBe("New User");
104
- expect(result.user.status).toBe("PENDING");
124
+ expect(result.ok).toBe(true);
125
+ if (result.ok) {
126
+ expect(result.value.user.email).toBe("new@example.com");
127
+ expect(result.value.user.name).toBe("New User");
128
+ expect(result.value.user.status).toBe("PENDING");
129
+ }
105
130
  expect(spies.insert).toHaveBeenCalled();
106
131
  });
107
132
 
@@ -121,7 +146,7 @@ describe("createUser", () => {
121
146
  spies.select.mockReturnValue(undefined);
122
147
  spies.insert.mockReturnValueOnce(createdUser).mockReturnValueOnce(createdAuditEvent);
123
148
 
124
- const result = await createUser(
149
+ const result = await run(
125
150
  db,
126
151
  {
127
152
  email: "test@example.com",
@@ -131,21 +156,15 @@ describe("createUser", () => {
131
156
  ctx,
132
157
  );
133
158
 
134
- expect(result.auditEvent.eventType).toBe("USER_CREATED");
135
- expect(result.auditEvent.actorId).toBe("actor-1");
136
- expect(result.auditEvent.targetId).toBe(createdUser.id);
137
- });
138
-
139
- it("throws when permission is missing", async () => {
140
- const { db } = createMockDb<DB>();
141
- const denied: CommandContext = { actorId: "test-actor", permissions: [] };
142
- await expect(
143
- createUser(db, { email: "test@example.com", name: "Test User", actorId: "actor-1" }, denied),
144
- ).rejects.toBeInstanceOf(InsufficientPermissionError);
159
+ expect(result.ok).toBe(true);
160
+ if (result.ok) {
161
+ expect(result.value.auditEvent.eventType).toBe("USER_CREATED");
162
+ expect(result.value.auditEvent.actorId).toBe("actor-1");
163
+ expect(result.value.auditEvent.targetId).toBe(createdUser.id);
164
+ }
145
165
  });
146
166
 
147
167
  it("passes custom fields through to insert", async () => {
148
- const createUserWithFields = makeCreateUser<{ department: string }>();
149
168
  const { db, spies } = createMockDb<DB>();
150
169
  const createdUser = {
151
170
  ...pendingUser,
@@ -166,7 +185,7 @@ describe("createUser", () => {
166
185
  spies.select.mockReturnValue(undefined);
167
186
  spies.insert.mockReturnValueOnce(createdUser).mockReturnValueOnce(createdAuditEvent);
168
187
 
169
- const result = await createUserWithFields(
188
+ const result = await run(
170
189
  db,
171
190
  {
172
191
  email: "new@example.com",
@@ -177,7 +196,10 @@ describe("createUser", () => {
177
196
  ctx,
178
197
  );
179
198
 
180
- expect(result.user.id).toBe("new-user-id");
199
+ expect(result.ok).toBe(true);
200
+ if (result.ok) {
201
+ expect(result.value.user.id).toBe("new-user-id");
202
+ }
181
203
  expect(spies.insert).toHaveBeenCalledTimes(2);
182
204
 
183
205
  // User insert includes custom field
@@ -1,11 +1,10 @@
1
- import { defineCommand } from "../../shared/internal";
1
+ import { ok, err, type CommandContext } from "../../shared/internal";
2
2
  import { DB } from "../generated/kysely-tailordb";
3
3
  import {
4
4
  InvalidEmailError,
5
5
  MissingRequiredFieldError,
6
6
  UserAlreadyExistsError,
7
- } from "../lib/errors";
8
- import { permissions } from "../permissions";
7
+ } from "../lib/errors.generated";
9
8
 
10
9
  interface CreateUserInput {
11
10
  email: string;
@@ -21,65 +20,64 @@ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
21
20
  * Creates a new user account in PENDING status.
22
21
  * Validates email format and uniqueness, and logs a USER_CREATED audit event.
23
22
  */
24
- export function makeCreateUser<CF extends Record<string, unknown>>() {
25
- return defineCommand(permissions.createUser, async (db: DB, input: CreateUserInput & CF) => {
26
- const { email, name, actorId, ...customFields } = input;
23
+ export async function run<CF extends Record<string, unknown>>(
24
+ db: DB,
25
+ input: CreateUserInput & CF,
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ _ctx: CommandContext,
28
+ ) {
29
+ const { email, name, actorId, ...customFields } = input;
27
30
 
28
- // 1. Validate email is provided
29
- if (!email || email.trim() === "") {
30
- throw new MissingRequiredFieldError("email");
31
- }
31
+ if (!email || email.trim() === "") {
32
+ return err(new MissingRequiredFieldError("email"));
33
+ }
32
34
 
33
- // 2. Validate name is provided
34
- if (!name || name.trim() === "") {
35
- throw new MissingRequiredFieldError("name");
36
- }
35
+ if (!name || name.trim() === "") {
36
+ return err(new MissingRequiredFieldError("name"));
37
+ }
37
38
 
38
- // 3. Validate email format
39
- if (!EMAIL_PATTERN.test(email)) {
40
- throw new InvalidEmailError(email);
41
- }
39
+ if (!EMAIL_PATTERN.test(email)) {
40
+ return err(new InvalidEmailError(email));
41
+ }
42
42
 
43
- // 4. Check if email already exists
44
- const existingUser = await db
45
- .selectFrom("User")
46
- .selectAll()
47
- .where("email", "=", email)
48
- .executeTakeFirst();
43
+ const existingUser = await db
44
+ .selectFrom("User")
45
+ .selectAll()
46
+ .where("email", "=", email)
47
+ .executeTakeFirst();
49
48
 
50
- if (existingUser) {
51
- throw new UserAlreadyExistsError(email);
52
- }
49
+ if (existingUser) {
50
+ return err(new UserAlreadyExistsError(email));
51
+ }
53
52
 
54
- // 5. Create user with PENDING status
55
- const user = await db
56
- .insertInto("User")
57
- .values({
58
- ...(customFields as Record<string, unknown>),
59
- email,
60
- name,
61
- status: "PENDING",
62
- createdAt: new Date(),
63
- updatedAt: null,
64
- })
65
- .returningAll()
66
- .executeTakeFirst();
53
+ // 5. Create user with PENDING status
54
+ const user = await db
55
+ .insertInto("User")
56
+ .values({
57
+ ...(customFields as Record<string, unknown>),
58
+ email,
59
+ name,
60
+ status: "PENDING",
61
+ createdAt: new Date(),
62
+ updatedAt: null,
63
+ })
64
+ .returningAll()
65
+ .executeTakeFirst();
67
66
 
68
- // 6. Log USER_CREATED audit event
69
- const auditEvent = await db
70
- .insertInto("AuditEvent")
71
- .values({
72
- eventType: "USER_CREATED",
73
- actorId,
74
- targetId: user!.id,
75
- targetType: "User",
76
- payload: JSON.stringify({ email, name, ...customFields }),
77
- createdAt: new Date(),
78
- updatedAt: null,
79
- })
80
- .returningAll()
81
- .executeTakeFirst();
67
+ // 6. Log USER_CREATED audit event
68
+ const auditEvent = await db
69
+ .insertInto("AuditEvent")
70
+ .values({
71
+ eventType: "USER_CREATED",
72
+ actorId,
73
+ targetId: user!.id,
74
+ targetType: "User",
75
+ payload: JSON.stringify({ email, name, ...customFields }),
76
+ createdAt: new Date(),
77
+ updatedAt: null,
78
+ })
79
+ .returningAll()
80
+ .executeTakeFirst();
82
81
 
83
- return { user: user!, auditEvent: auditEvent! };
84
- });
82
+ return ok({ user: user!, auditEvent: auditEvent! });
85
83
  }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { defineCommand } from "../../shared/internal";
3
+ import { permissions } from "../lib/permissions.generated";
4
+ import { run } from "./deactivateUser";
5
+
6
+ export const deactivateUser = defineCommand(permissions.deactivateUser, run);
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { createMockDb, testNotFound, testPermissionDenied } from "../../testing/index";
2
+ import { createMockDb, testNotFound } from "../../testing/index";
3
3
  import { type CommandContext } from "../../shared/internal";
4
4
  import { DB } from "../generated/kysely-tailordb";
5
- import { InvalidStatusTransitionError, UserNotFoundError } from "../lib/errors";
5
+ import { InvalidStatusTransitionError, UserNotFoundError } from "../lib/errors.generated";
6
6
  import { activeUser, baseAuditEvent, inactiveUser, pendingUser } from "../testing/fixtures";
7
- import { deactivateUser } from "./deactivateUser";
7
+ import { run } from "./deactivateUser";
8
8
 
9
9
  describe("deactivateUser", () => {
10
10
  const ctx: CommandContext = {
@@ -14,30 +14,29 @@ describe("deactivateUser", () => {
14
14
 
15
15
  it(
16
16
  "throws when user does not exist",
17
- testNotFound(
18
- deactivateUser,
19
- { userId: "nonexistent-user", actorId: "actor-1" },
20
- ctx,
21
- UserNotFoundError,
22
- ),
17
+ testNotFound(run, { userId: "nonexistent-user", actorId: "actor-1" }, ctx, UserNotFoundError),
23
18
  );
24
19
 
25
20
  it("throws when user is PENDING", async () => {
26
21
  const { db, spies } = createMockDb<DB>();
27
22
  spies.select.mockReturnValue(pendingUser);
28
23
 
29
- await expect(
30
- deactivateUser(db, { userId: pendingUser.id, actorId: "actor-1" }, ctx),
31
- ).rejects.toBeInstanceOf(InvalidStatusTransitionError);
24
+ const result = await run(db, { userId: pendingUser.id, actorId: "actor-1" }, ctx);
25
+ expect(result.ok).toBe(false);
26
+ if (!result.ok) {
27
+ expect(result.error).toBeInstanceOf(InvalidStatusTransitionError);
28
+ }
32
29
  });
33
30
 
34
31
  it("throws when user is already INACTIVE", async () => {
35
32
  const { db, spies } = createMockDb<DB>();
36
33
  spies.select.mockReturnValue(inactiveUser);
37
34
 
38
- await expect(
39
- deactivateUser(db, { userId: inactiveUser.id, actorId: "actor-1" }, ctx),
40
- ).rejects.toBeInstanceOf(InvalidStatusTransitionError);
35
+ const result = await run(db, { userId: inactiveUser.id, actorId: "actor-1" }, ctx);
36
+ expect(result.ok).toBe(false);
37
+ if (!result.ok) {
38
+ expect(result.error).toBeInstanceOf(InvalidStatusTransitionError);
39
+ }
41
40
  });
42
41
 
43
42
  // Success cases
@@ -59,7 +58,7 @@ describe("deactivateUser", () => {
59
58
  spies.update.mockReturnValue(deactivatedUser);
60
59
  spies.insert.mockReturnValue(createdAuditEvent);
61
60
 
62
- const result = await deactivateUser(
61
+ const result = await run(
63
62
  db,
64
63
  {
65
64
  userId: activeUser.id,
@@ -68,8 +67,11 @@ describe("deactivateUser", () => {
68
67
  ctx,
69
68
  );
70
69
 
71
- expect(result.user.status).toBe("INACTIVE");
72
- expect(result.user.updatedAt).not.toBeNull();
70
+ expect(result.ok).toBe(true);
71
+ if (result.ok) {
72
+ expect(result.value.user.status).toBe("INACTIVE");
73
+ expect(result.value.user.updatedAt).not.toBeNull();
74
+ }
73
75
  expect(spies.update).toHaveBeenCalled();
74
76
  });
75
77
 
@@ -91,7 +93,7 @@ describe("deactivateUser", () => {
91
93
  spies.update.mockReturnValue(deactivatedUser);
92
94
  spies.insert.mockReturnValue(createdAuditEvent);
93
95
 
94
- const result = await deactivateUser(
96
+ const result = await run(
95
97
  db,
96
98
  {
97
99
  userId: activeUser.id,
@@ -100,13 +102,11 @@ describe("deactivateUser", () => {
100
102
  ctx,
101
103
  );
102
104
 
103
- expect(result.auditEvent.eventType).toBe("USER_DEACTIVATED");
104
- expect(result.auditEvent.actorId).toBe("actor-1");
105
- expect(result.auditEvent.targetId).toBe(activeUser.id);
105
+ expect(result.ok).toBe(true);
106
+ if (result.ok) {
107
+ expect(result.value.auditEvent.eventType).toBe("USER_DEACTIVATED");
108
+ expect(result.value.auditEvent.actorId).toBe("actor-1");
109
+ expect(result.value.auditEvent.targetId).toBe(activeUser.id);
110
+ }
106
111
  });
107
-
108
- it(
109
- "throws when permission is missing",
110
- testPermissionDenied(deactivateUser, { userId: "some-user", actorId: "actor-1" }),
111
- );
112
112
  });