@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,8 +1,8 @@
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 { RoleNotFoundError, UserNotActiveError, UserNotFoundError } from "../lib/errors";
5
+ import { RoleNotFoundError, UserNotActiveError, UserNotFoundError } from "../lib/errors.generated";
6
6
  import {
7
7
  activeUser,
8
8
  baseAuditEvent,
@@ -11,7 +11,7 @@ import {
11
11
  inactiveUser,
12
12
  pendingUser,
13
13
  } from "../testing/fixtures";
14
- import { assignRoleToUser } from "./assignRoleToUser";
14
+ import { run } from "./assignRoleToUser";
15
15
 
16
16
  describe("assignRoleToUser", () => {
17
17
  const ctx: CommandContext = {
@@ -23,68 +23,76 @@ describe("assignRoleToUser", () => {
23
23
  const { db, spies } = createMockDb<DB>();
24
24
  spies.select.mockReturnValue(undefined);
25
25
 
26
- await expect(
27
- assignRoleToUser(
28
- db,
29
- {
30
- userId: "nonexistent-user",
31
- roleId: baseRole.id,
32
- actorId: "actor-1",
33
- },
34
- ctx,
35
- ),
36
- ).rejects.toBeInstanceOf(UserNotFoundError);
26
+ const result = await run(
27
+ db,
28
+ {
29
+ userId: "nonexistent-user",
30
+ roleId: baseRole.id,
31
+ actorId: "actor-1",
32
+ },
33
+ ctx,
34
+ );
35
+ expect(result.ok).toBe(false);
36
+ if (!result.ok) {
37
+ expect(result.error).toBeInstanceOf(UserNotFoundError);
38
+ }
37
39
  });
38
40
 
39
41
  it("throws when role does not exist", async () => {
40
42
  const { db, spies } = createMockDb<DB>();
41
43
  spies.select.mockReturnValueOnce(activeUser).mockReturnValueOnce(undefined);
42
44
 
43
- await expect(
44
- assignRoleToUser(
45
- db,
46
- {
47
- userId: activeUser.id,
48
- roleId: "nonexistent-role",
49
- actorId: "actor-1",
50
- },
51
- ctx,
52
- ),
53
- ).rejects.toBeInstanceOf(RoleNotFoundError);
45
+ const result = await run(
46
+ db,
47
+ {
48
+ userId: activeUser.id,
49
+ roleId: "nonexistent-role",
50
+ actorId: "actor-1",
51
+ },
52
+ ctx,
53
+ );
54
+ expect(result.ok).toBe(false);
55
+ if (!result.ok) {
56
+ expect(result.error).toBeInstanceOf(RoleNotFoundError);
57
+ }
54
58
  });
55
59
 
56
60
  it("throws when user is PENDING", async () => {
57
61
  const { db, spies } = createMockDb<DB>();
58
62
  spies.select.mockReturnValueOnce(pendingUser).mockReturnValueOnce(baseRole);
59
63
 
60
- await expect(
61
- assignRoleToUser(
62
- db,
63
- {
64
- userId: pendingUser.id,
65
- roleId: baseRole.id,
66
- actorId: "actor-1",
67
- },
68
- ctx,
69
- ),
70
- ).rejects.toBeInstanceOf(UserNotActiveError);
64
+ const result = await run(
65
+ db,
66
+ {
67
+ userId: pendingUser.id,
68
+ roleId: baseRole.id,
69
+ actorId: "actor-1",
70
+ },
71
+ ctx,
72
+ );
73
+ expect(result.ok).toBe(false);
74
+ if (!result.ok) {
75
+ expect(result.error).toBeInstanceOf(UserNotActiveError);
76
+ }
71
77
  });
72
78
 
73
79
  it("throws when user is INACTIVE", async () => {
74
80
  const { db, spies } = createMockDb<DB>();
75
81
  spies.select.mockReturnValueOnce(inactiveUser).mockReturnValueOnce(baseRole);
76
82
 
77
- await expect(
78
- assignRoleToUser(
79
- db,
80
- {
81
- userId: inactiveUser.id,
82
- roleId: baseRole.id,
83
- actorId: "actor-1",
84
- },
85
- ctx,
86
- ),
87
- ).rejects.toBeInstanceOf(UserNotActiveError);
83
+ const result = await run(
84
+ db,
85
+ {
86
+ userId: inactiveUser.id,
87
+ roleId: baseRole.id,
88
+ actorId: "actor-1",
89
+ },
90
+ ctx,
91
+ );
92
+ expect(result.ok).toBe(false);
93
+ if (!result.ok) {
94
+ expect(result.error).toBeInstanceOf(UserNotActiveError);
95
+ }
88
96
  });
89
97
 
90
98
  it("returns success when assignment already exists (idempotent)", async () => {
@@ -97,7 +105,7 @@ describe("assignRoleToUser", () => {
97
105
  .mockReturnValueOnce([{ key: "orders:read" }]); // Permission keys from recompute
98
106
  spies.update.mockReturnValueOnce(updatedUser);
99
107
 
100
- const result = await assignRoleToUser(
108
+ const result = await run(
101
109
  db,
102
110
  {
103
111
  userId: activeUser.id,
@@ -107,8 +115,11 @@ describe("assignRoleToUser", () => {
107
115
  ctx,
108
116
  );
109
117
 
110
- expect(result.userRole).toEqual(baseUserRole);
111
- expect(result.user.permissions).toEqual(["orders:read"]);
118
+ expect(result.ok).toBe(true);
119
+ if (result.ok) {
120
+ expect(result.value.userRole).toEqual(baseUserRole);
121
+ expect(result.value.user.permissions).toEqual(["orders:read"]);
122
+ }
112
123
  expect(spies.insert).not.toHaveBeenCalled();
113
124
  });
114
125
 
@@ -135,7 +146,7 @@ describe("assignRoleToUser", () => {
135
146
  spies.insert.mockReturnValueOnce(newUserRole).mockReturnValueOnce(createdAuditEvent);
136
147
  spies.update.mockReturnValueOnce(updatedUser);
137
148
 
138
- const result = await assignRoleToUser(
149
+ const result = await run(
139
150
  db,
140
151
  {
141
152
  userId: activeUser.id,
@@ -145,18 +156,14 @@ describe("assignRoleToUser", () => {
145
156
  ctx,
146
157
  );
147
158
 
148
- expect(result.userRole.userId).toBe(activeUser.id);
149
- expect(result.userRole.roleId).toBe(baseRole.id);
150
- expect(result.auditEvent?.eventType).toBe("ROLE_ASSIGNED");
151
- expect(result.user.permissions).toEqual(["orders:read"]);
159
+ expect(result.ok).toBe(true);
160
+ if (result.ok) {
161
+ expect(result.value.userRole.userId).toBe(activeUser.id);
162
+ expect(result.value.userRole.roleId).toBe(baseRole.id);
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
164
+ expect((result.value as any).auditEvent?.eventType).toBe("ROLE_ASSIGNED");
165
+ expect(result.value.user.permissions).toEqual(["orders:read"]);
166
+ }
152
167
  expect(spies.insert).toHaveBeenCalled();
153
168
  });
154
-
155
- it("throws when permission is missing", async () => {
156
- const { db } = createMockDb<DB>();
157
- const denied: CommandContext = { actorId: "test-actor", permissions: [] };
158
- await expect(
159
- assignRoleToUser(db, { userId: "user-1", roleId: "role-1", actorId: "actor-1" }, denied),
160
- ).rejects.toBeInstanceOf(InsufficientPermissionError);
161
- });
162
169
  });
@@ -1,8 +1,7 @@
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 { RoleNotFoundError, UserNotActiveError, UserNotFoundError } from "../lib/errors";
3
+ import { RoleNotFoundError, UserNotActiveError, UserNotFoundError } from "../lib/errors.generated";
4
4
  import { recomputeUserPermissions } from "../lib/recomputeUserPermissions";
5
- import { permissions } from "../permissions";
6
5
 
7
6
  export interface AssignRoleToUserInput {
8
7
  userId: string;
@@ -17,77 +16,75 @@ export interface AssignRoleToUserInput {
17
16
  * Operation is idempotent - assigning the same role twice does not create
18
17
  * duplicate records or error.
19
18
  */
20
- export const assignRoleToUser = defineCommand(
21
- permissions.assignRoleToUser,
22
- async (db: DB, input: AssignRoleToUserInput) => {
23
- const user = await db
24
- .selectFrom("User")
25
- .selectAll()
26
- .where("id", "=", input.userId)
27
- .executeTakeFirst();
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ export async function run(db: DB, input: AssignRoleToUserInput, _ctx: CommandContext) {
21
+ const user = await db
22
+ .selectFrom("User")
23
+ .selectAll()
24
+ .where("id", "=", input.userId)
25
+ .executeTakeFirst();
28
26
 
29
- if (!user) {
30
- throw new UserNotFoundError(input.userId);
31
- }
27
+ if (!user) {
28
+ return err(new UserNotFoundError(input.userId));
29
+ }
32
30
 
33
- const role = await db
34
- .selectFrom("Role")
35
- .selectAll()
36
- .where("id", "=", input.roleId)
37
- .executeTakeFirst();
31
+ const role = await db
32
+ .selectFrom("Role")
33
+ .selectAll()
34
+ .where("id", "=", input.roleId)
35
+ .executeTakeFirst();
38
36
 
39
- if (!role) {
40
- throw new RoleNotFoundError(input.roleId);
41
- }
37
+ if (!role) {
38
+ return err(new RoleNotFoundError(input.roleId));
39
+ }
42
40
 
43
- if (user.status !== "ACTIVE") {
44
- throw new UserNotActiveError(input.userId, user.status);
45
- }
41
+ if (user.status !== "ACTIVE") {
42
+ return err(new UserNotActiveError(`${input.userId} (status: ${user.status})`));
43
+ }
46
44
 
47
- // Idempotent: return existing assignment, recompute permissions for consistency
48
- const existingAssignment = await db
49
- .selectFrom("UserRole")
50
- .selectAll()
51
- .where("userId", "=", input.userId)
52
- .where("roleId", "=", input.roleId)
53
- .executeTakeFirst();
45
+ // Idempotent: return existing assignment, recompute permissions for consistency
46
+ const existingAssignment = await db
47
+ .selectFrom("UserRole")
48
+ .selectAll()
49
+ .where("userId", "=", input.userId)
50
+ .where("roleId", "=", input.roleId)
51
+ .executeTakeFirst();
54
52
 
55
- if (existingAssignment) {
56
- const updatedUser = await recomputeUserPermissions(db, input.userId);
57
- return { userRole: existingAssignment, user: updatedUser };
58
- }
53
+ if (existingAssignment) {
54
+ const updatedUser = await recomputeUserPermissions(db, input.userId);
55
+ return ok({ userRole: existingAssignment, user: updatedUser });
56
+ }
57
+
58
+ const userRole = await db
59
+ .insertInto("UserRole")
60
+ .values({
61
+ userId: input.userId,
62
+ roleId: input.roleId,
63
+ createdAt: new Date(),
64
+ updatedAt: null,
65
+ })
66
+ .returningAll()
67
+ .executeTakeFirst();
59
68
 
60
- const userRole = await db
61
- .insertInto("UserRole")
62
- .values({
69
+ const auditEvent = await db
70
+ .insertInto("AuditEvent")
71
+ .values({
72
+ eventType: "ROLE_ASSIGNED",
73
+ actorId: input.actorId,
74
+ targetId: input.userId,
75
+ targetType: "User",
76
+ payload: JSON.stringify({
63
77
  userId: input.userId,
64
78
  roleId: input.roleId,
65
- createdAt: new Date(),
66
- updatedAt: null,
67
- })
68
- .returningAll()
69
- .executeTakeFirst();
79
+ roleName: role.name,
80
+ }),
81
+ createdAt: new Date(),
82
+ updatedAt: null,
83
+ })
84
+ .returningAll()
85
+ .executeTakeFirst();
70
86
 
71
- const auditEvent = await db
72
- .insertInto("AuditEvent")
73
- .values({
74
- eventType: "ROLE_ASSIGNED",
75
- actorId: input.actorId,
76
- targetId: input.userId,
77
- targetType: "User",
78
- payload: JSON.stringify({
79
- userId: input.userId,
80
- roleId: input.roleId,
81
- roleName: role.name,
82
- }),
83
- createdAt: new Date(),
84
- updatedAt: null,
85
- })
86
- .returningAll()
87
- .executeTakeFirst();
87
+ const updatedUser = await recomputeUserPermissions(db, input.userId);
88
88
 
89
- const updatedUser = await recomputeUserPermissions(db, input.userId);
90
-
91
- return { userRole: userRole!, user: updatedUser, auditEvent: auditEvent! };
92
- },
93
- );
89
+ return ok({ userRole: userRole!, user: updatedUser, auditEvent: auditEvent! });
90
+ }
@@ -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 "./createPermission";
5
+
6
+ export const createPermission = defineCommand(permissions.createPermission, 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
  DuplicatePermissionKeyError,
7
7
  InvalidPermissionKeyFormatError,
8
8
  MissingRequiredFieldError,
9
- } from "../lib/errors";
9
+ } from "../lib/errors.generated";
10
10
  import { basePermission } from "../testing/fixtures";
11
- import { makeCreatePermission } from "./createPermission";
12
-
13
- const createPermission = makeCreatePermission();
11
+ import { run } from "./createPermission";
14
12
 
15
13
  describe("createPermission", () => {
16
14
  const ctx: CommandContext = {
@@ -22,50 +20,62 @@ describe("createPermission", () => {
22
20
  it("throws when key is empty", async () => {
23
21
  const { db } = createMockDb<DB>();
24
22
 
25
- await expect(createPermission(db, { key: "" }, ctx)).rejects.toBeInstanceOf(
26
- MissingRequiredFieldError,
27
- );
23
+ const result = await run(db, { key: "" }, 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 key format is invalid (no colon)", async () => {
31
31
  const { db } = createMockDb<DB>();
32
32
 
33
- await expect(createPermission(db, { key: "invalidkey" }, ctx)).rejects.toBeInstanceOf(
34
- InvalidPermissionKeyFormatError,
35
- );
33
+ const result = await run(db, { key: "invalidkey" }, ctx);
34
+ expect(result.ok).toBe(false);
35
+ if (!result.ok) {
36
+ expect(result.error).toBeInstanceOf(InvalidPermissionKeyFormatError);
37
+ }
36
38
  });
37
39
 
38
40
  it("throws when key format is invalid (multiple colons)", async () => {
39
41
  const { db } = createMockDb<DB>();
40
42
 
41
- await expect(
42
- createPermission(db, { key: "resource:action:extra" }, ctx),
43
- ).rejects.toBeInstanceOf(InvalidPermissionKeyFormatError);
43
+ const result = await run(db, { key: "resource:action:extra" }, ctx);
44
+ expect(result.ok).toBe(false);
45
+ if (!result.ok) {
46
+ expect(result.error).toBeInstanceOf(InvalidPermissionKeyFormatError);
47
+ }
44
48
  });
45
49
 
46
50
  it("throws when key format is invalid (empty resource)", async () => {
47
51
  const { db } = createMockDb<DB>();
48
52
 
49
- await expect(createPermission(db, { key: ":action" }, ctx)).rejects.toBeInstanceOf(
50
- InvalidPermissionKeyFormatError,
51
- );
53
+ const result = await run(db, { key: ":action" }, ctx);
54
+ expect(result.ok).toBe(false);
55
+ if (!result.ok) {
56
+ expect(result.error).toBeInstanceOf(InvalidPermissionKeyFormatError);
57
+ }
52
58
  });
53
59
 
54
60
  it("throws when key format is invalid (empty action)", async () => {
55
61
  const { db } = createMockDb<DB>();
56
62
 
57
- await expect(createPermission(db, { key: "resource:" }, ctx)).rejects.toBeInstanceOf(
58
- InvalidPermissionKeyFormatError,
59
- );
63
+ const result = await run(db, { key: "resource:" }, ctx);
64
+ expect(result.ok).toBe(false);
65
+ if (!result.ok) {
66
+ expect(result.error).toBeInstanceOf(InvalidPermissionKeyFormatError);
67
+ }
60
68
  });
61
69
 
62
70
  it("throws when permission key already exists", async () => {
63
71
  const { db, spies } = createMockDb<DB>();
64
72
  spies.select.mockReturnValue(basePermission);
65
73
 
66
- await expect(createPermission(db, { key: basePermission.key }, ctx)).rejects.toBeInstanceOf(
67
- DuplicatePermissionKeyError,
68
- );
74
+ const result = await run(db, { key: basePermission.key }, ctx);
75
+ expect(result.ok).toBe(false);
76
+ if (!result.ok) {
77
+ expect(result.error).toBeInstanceOf(DuplicatePermissionKeyError);
78
+ }
69
79
  });
70
80
 
71
81
  // Success cases
@@ -81,9 +91,12 @@ describe("createPermission", () => {
81
91
  spies.select.mockReturnValue(undefined); // No existing permission
82
92
  spies.insert.mockReturnValue(createdPermission);
83
93
 
84
- const result = await createPermission(db, { key: "users:read" }, ctx);
94
+ const result = await run(db, { key: "users:read" }, ctx);
85
95
 
86
- expect(result.permission.key).toBe("users:read");
96
+ expect(result.ok).toBe(true);
97
+ if (result.ok) {
98
+ expect(result.value.permission.key).toBe("users:read");
99
+ }
87
100
  expect(spies.insert).toHaveBeenCalled();
88
101
  });
89
102
 
@@ -99,7 +112,7 @@ describe("createPermission", () => {
99
112
  spies.select.mockReturnValue(undefined);
100
113
  spies.insert.mockReturnValue(createdPermission);
101
114
 
102
- const result = await createPermission(
115
+ const result = await run(
103
116
  db,
104
117
  {
105
118
  key: "users:write",
@@ -108,20 +121,14 @@ describe("createPermission", () => {
108
121
  ctx,
109
122
  );
110
123
 
111
- expect(result.permission.key).toBe("users:write");
112
- expect(result.permission.description).toBe("Permission to write users");
113
- });
114
-
115
- it("throws when permission is missing", async () => {
116
- const { db } = createMockDb<DB>();
117
- const denied: CommandContext = { actorId: "test-actor", permissions: [] };
118
- await expect(createPermission(db, { key: "users:read" }, denied)).rejects.toBeInstanceOf(
119
- InsufficientPermissionError,
120
- );
124
+ expect(result.ok).toBe(true);
125
+ if (result.ok) {
126
+ expect(result.value.permission.key).toBe("users:write");
127
+ expect(result.value.permission.description).toBe("Permission to write users");
128
+ }
121
129
  });
122
130
 
123
131
  it("passes custom fields through to insert", async () => {
124
- const createPermissionWithFields = makeCreatePermission<{ module: string }>();
125
132
  const { db, spies } = createMockDb<DB>();
126
133
  const createdPermission = {
127
134
  ...basePermission,
@@ -133,7 +140,7 @@ describe("createPermission", () => {
133
140
  spies.select.mockReturnValue(undefined);
134
141
  spies.insert.mockReturnValue(createdPermission);
135
142
 
136
- await createPermissionWithFields(db, { key: "users:read", module: "user-management" }, ctx);
143
+ await run(db, { key: "users:read", module: "user-management" }, ctx);
137
144
 
138
145
  expect(spies.values).toHaveBeenNthCalledWith(
139
146
  1,
@@ -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
  DuplicatePermissionKeyError,
5
5
  InvalidPermissionKeyFormatError,
6
6
  MissingRequiredFieldError,
7
- } from "../lib/errors";
8
- import { permissions } from "../permissions";
7
+ } from "../lib/errors.generated";
9
8
 
10
9
  interface CreatePermissionInput {
11
10
  key: string;
@@ -20,47 +19,44 @@ const PERMISSION_KEY_PATTERN = /^[a-zA-Z0-9_-]+:[a-zA-Z0-9_-]+$/;
20
19
  * Creates a new permission with the specified key in resource:action format.
21
20
  * Validates that the key follows the correct format and is unique.
22
21
  */
23
- export function makeCreatePermission<CF extends Record<string, unknown>>() {
24
- return defineCommand(
25
- permissions.createPermission,
26
- async (db: DB, input: CreatePermissionInput & CF) => {
27
- const { key, description, ...customFields } = input;
28
-
29
- // 1. Validate key is provided
30
- if (!key || key.trim() === "") {
31
- throw new MissingRequiredFieldError("key");
32
- }
33
-
34
- // 2. Validate key format (resource:action)
35
- if (!PERMISSION_KEY_PATTERN.test(key)) {
36
- throw new InvalidPermissionKeyFormatError(key);
37
- }
38
-
39
- // 3. Check if permission key already exists
40
- const existingPermission = await db
41
- .selectFrom("Permission")
42
- .selectAll()
43
- .where("key", "=", key)
44
- .executeTakeFirst();
45
-
46
- if (existingPermission) {
47
- throw new DuplicatePermissionKeyError(key);
48
- }
49
-
50
- // 4. Create permission
51
- const permission = await db
52
- .insertInto("Permission")
53
- .values({
54
- ...(customFields as Record<string, unknown>),
55
- key,
56
- description: description ?? null,
57
- createdAt: new Date(),
58
- updatedAt: null,
59
- })
60
- .returningAll()
61
- .executeTakeFirst();
62
-
63
- return { permission: permission! };
64
- },
65
- );
22
+ export async function run<CF extends Record<string, unknown>>(
23
+ db: DB,
24
+ input: CreatePermissionInput & CF,
25
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
+ _ctx: CommandContext,
27
+ ) {
28
+ const { key, description, ...customFields } = input;
29
+
30
+ if (!key || key.trim() === "") {
31
+ return err(new MissingRequiredFieldError("key"));
32
+ }
33
+
34
+ if (!PERMISSION_KEY_PATTERN.test(key)) {
35
+ return err(new InvalidPermissionKeyFormatError(key));
36
+ }
37
+
38
+ const existingPermission = await db
39
+ .selectFrom("Permission")
40
+ .selectAll()
41
+ .where("key", "=", key)
42
+ .executeTakeFirst();
43
+
44
+ if (existingPermission) {
45
+ return err(new DuplicatePermissionKeyError(key));
46
+ }
47
+
48
+ // 4. Create permission
49
+ const permission = await db
50
+ .insertInto("Permission")
51
+ .values({
52
+ ...(customFields as Record<string, unknown>),
53
+ key,
54
+ description: description ?? null,
55
+ createdAt: new Date(),
56
+ updatedAt: null,
57
+ })
58
+ .returningAll()
59
+ .executeTakeFirst();
60
+
61
+ return ok({ permission: permission! });
66
62
  }
@@ -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 "./createRole";
5
+
6
+ export const createRole = defineCommand(permissions.createRole, run);