ddd-ts-kit 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/README.md +31 -2
  2. package/dist/check/catalogue/architecture-rules.d.ts.map +1 -1
  3. package/dist/check/catalogue/architecture-rules.js +2 -0
  4. package/dist/check/catalogue/architecture-rules.js.map +1 -1
  5. package/dist/check/explain/domain-docs.d.ts +3 -0
  6. package/dist/check/explain/domain-docs.d.ts.map +1 -0
  7. package/dist/check/explain/domain-docs.js +30 -0
  8. package/dist/check/explain/domain-docs.js.map +1 -0
  9. package/dist/check/explain/rule-docs.d.ts.map +1 -1
  10. package/dist/check/explain/rule-docs.js +2 -0
  11. package/dist/check/explain/rule-docs.js.map +1 -1
  12. package/dist/check/explain/tenancy-docs.d.ts.map +1 -1
  13. package/dist/check/explain/tenancy-docs.js +3 -1
  14. package/dist/check/explain/tenancy-docs.js.map +1 -1
  15. package/dist/check/fixtures/fixtures.d.ts.map +1 -1
  16. package/dist/check/fixtures/fixtures.js +4 -0
  17. package/dist/check/fixtures/fixtures.js.map +1 -1
  18. package/dist/check/fixtures/model-function-fixtures.d.ts +4 -0
  19. package/dist/check/fixtures/model-function-fixtures.d.ts.map +1 -0
  20. package/dist/check/fixtures/model-function-fixtures.js +52 -0
  21. package/dist/check/fixtures/model-function-fixtures.js.map +1 -0
  22. package/dist/check/fixtures/tenancy-fixtures.d.ts.map +1 -1
  23. package/dist/check/fixtures/tenancy-fixtures.js +28 -0
  24. package/dist/check/fixtures/tenancy-fixtures.js.map +1 -1
  25. package/dist/check/fixtures/value-object-file-fixtures.d.ts +4 -0
  26. package/dist/check/fixtures/value-object-file-fixtures.d.ts.map +1 -0
  27. package/dist/check/fixtures/value-object-file-fixtures.js +54 -0
  28. package/dist/check/fixtures/value-object-file-fixtures.js.map +1 -0
  29. package/dist/check/fixtures/value-object-files.d.ts +34 -0
  30. package/dist/check/fixtures/value-object-files.d.ts.map +1 -0
  31. package/dist/check/fixtures/value-object-files.js +46 -0
  32. package/dist/check/fixtures/value-object-files.js.map +1 -0
  33. package/dist/check/rules/domain-value-object-class.d.ts +3 -0
  34. package/dist/check/rules/domain-value-object-class.d.ts.map +1 -0
  35. package/dist/check/rules/domain-value-object-class.js +21 -0
  36. package/dist/check/rules/domain-value-object-class.js.map +1 -0
  37. package/dist/check/structure/class-body-brace.d.ts +6 -0
  38. package/dist/check/structure/class-body-brace.d.ts.map +1 -0
  39. package/dist/check/structure/class-body-brace.js +22 -0
  40. package/dist/check/structure/class-body-brace.js.map +1 -0
  41. package/dist/check/structure/read-structure.d.ts.map +1 -1
  42. package/dist/check/structure/read-structure.js +3 -1
  43. package/dist/check/structure/read-structure.js.map +1 -1
  44. package/dist/check/tenancy/knex-unscoped.d.ts +6 -1
  45. package/dist/check/tenancy/knex-unscoped.d.ts.map +1 -1
  46. package/dist/check/tenancy/knex-unscoped.js +19 -6
  47. package/dist/check/tenancy/knex-unscoped.js.map +1 -1
  48. package/dist/check/tenancy/raw-reaches-rows.d.ts +8 -0
  49. package/dist/check/tenancy/raw-reaches-rows.d.ts.map +1 -0
  50. package/dist/check/tenancy/raw-reaches-rows.js +91 -0
  51. package/dist/check/tenancy/raw-reaches-rows.js.map +1 -0
  52. package/dist/check/tenancy/raw-sql-text.d.ts +9 -0
  53. package/dist/check/tenancy/raw-sql-text.d.ts.map +1 -0
  54. package/dist/check/tenancy/raw-sql-text.js +29 -0
  55. package/dist/check/tenancy/raw-sql-text.js.map +1 -0
  56. package/dist/check/values/answered-literals.d.ts +8 -0
  57. package/dist/check/values/answered-literals.d.ts.map +1 -0
  58. package/dist/check/values/answered-literals.js +20 -0
  59. package/dist/check/values/answered-literals.js.map +1 -0
  60. package/dist/check/values/arrow-body.d.ts +4 -0
  61. package/dist/check/values/arrow-body.d.ts.map +1 -0
  62. package/dist/check/values/arrow-body.js +52 -0
  63. package/dist/check/values/arrow-body.js.map +1 -0
  64. package/dist/check/values/entry-role.d.ts +10 -0
  65. package/dist/check/values/entry-role.d.ts.map +1 -0
  66. package/dist/check/values/entry-role.js +40 -0
  67. package/dist/check/values/entry-role.js.map +1 -0
  68. package/dist/check/values/exported-function-body.d.ts +9 -0
  69. package/dist/check/values/exported-function-body.d.ts.map +1 -0
  70. package/dist/check/values/exported-function-body.js +29 -0
  71. package/dist/check/values/exported-function-body.js.map +1 -0
  72. package/dist/check/values/exported-values.d.ts +9 -0
  73. package/dist/check/values/exported-values.d.ts.map +1 -0
  74. package/dist/check/values/exported-values.js +48 -0
  75. package/dist/check/values/exported-values.js.map +1 -0
  76. package/dist/check/values/extends-value-object.d.ts +9 -0
  77. package/dist/check/values/extends-value-object.d.ts.map +1 -0
  78. package/dist/check/values/extends-value-object.js +25 -0
  79. package/dist/check/values/extends-value-object.js.map +1 -0
  80. package/dist/check/values/function-body.d.ts +10 -0
  81. package/dist/check/values/function-body.d.ts.map +1 -0
  82. package/dist/check/values/function-body.js +2 -0
  83. package/dist/check/values/function-body.js.map +1 -0
  84. package/dist/check/values/generate-hint.d.ts +4 -0
  85. package/dist/check/values/generate-hint.d.ts.map +1 -0
  86. package/dist/check/values/generate-hint.js +8 -0
  87. package/dist/check/values/generate-hint.js.map +1 -0
  88. package/dist/check/values/initializer-of.d.ts +3 -0
  89. package/dist/check/values/initializer-of.d.ts.map +1 -0
  90. package/dist/check/values/initializer-of.js +15 -0
  91. package/dist/check/values/initializer-of.js.map +1 -0
  92. package/dist/check/values/is-model-file.d.ts +8 -0
  93. package/dist/check/values/is-model-file.d.ts.map +1 -0
  94. package/dist/check/values/is-model-file.js +15 -0
  95. package/dist/check/values/is-model-file.js.map +1 -0
  96. package/dist/check/values/is-value-object-file.d.ts +7 -0
  97. package/dist/check/values/is-value-object-file.d.ts.map +1 -0
  98. package/dist/check/values/is-value-object-file.js +14 -0
  99. package/dist/check/values/is-value-object-file.js.map +1 -0
  100. package/dist/check/values/javascript-globals.d.ts +3 -0
  101. package/dist/check/values/javascript-globals.d.ts.map +1 -0
  102. package/dist/check/values/javascript-globals.js +22 -0
  103. package/dist/check/values/javascript-globals.js.map +1 -0
  104. package/dist/check/values/literal-at.d.ts +3 -0
  105. package/dist/check/values/literal-at.d.ts.map +1 -0
  106. package/dist/check/values/literal-at.js +7 -0
  107. package/dist/check/values/literal-at.js.map +1 -0
  108. package/dist/check/values/local-declaration.d.ts +8 -0
  109. package/dist/check/values/local-declaration.d.ts.map +1 -0
  110. package/dist/check/values/local-declaration.js +31 -0
  111. package/dist/check/values/local-declaration.js.map +1 -0
  112. package/dist/check/values/nested-bodies.d.ts +8 -0
  113. package/dist/check/values/nested-bodies.d.ts.map +1 -0
  114. package/dist/check/values/nested-bodies.js +28 -0
  115. package/dist/check/values/nested-bodies.js.map +1 -0
  116. package/dist/check/values/plain-value-functions.d.ts +10 -0
  117. package/dist/check/values/plain-value-functions.d.ts.map +1 -0
  118. package/dist/check/values/plain-value-functions.js +33 -0
  119. package/dist/check/values/plain-value-functions.js.map +1 -0
  120. package/dist/check/values/returns-package-type.d.ts +9 -0
  121. package/dist/check/values/returns-package-type.d.ts.map +1 -0
  122. package/dist/check/values/returns-package-type.js +12 -0
  123. package/dist/check/values/returns-package-type.js.map +1 -0
  124. package/dist/check/values/stands-for-a-value.d.ts +9 -0
  125. package/dist/check/values/stands-for-a-value.d.ts.map +1 -0
  126. package/dist/check/values/stands-for-a-value.js +28 -0
  127. package/dist/check/values/stands-for-a-value.js.map +1 -0
  128. package/dist/check/values/value-object-file-findings.d.ts +5 -0
  129. package/dist/check/values/value-object-file-findings.d.ts.map +1 -0
  130. package/dist/check/values/value-object-file-findings.js +96 -0
  131. package/dist/check/values/value-object-file-findings.js.map +1 -0
  132. package/dist/commands/generate.js +2 -0
  133. package/dist/commands/generate.js.map +1 -1
  134. package/dist/commands/init.d.ts.map +1 -1
  135. package/dist/commands/init.js +12 -1
  136. package/dist/commands/init.js.map +1 -1
  137. package/dist/config/index.d.ts +1 -1
  138. package/dist/config/index.d.ts.map +1 -1
  139. package/dist/config/index.js +1 -1
  140. package/dist/config/index.js.map +1 -1
  141. package/dist/config/parse-stack.d.ts.map +1 -1
  142. package/dist/config/parse-stack.js +2 -1
  143. package/dist/config/parse-stack.js.map +1 -1
  144. package/dist/config/stack.d.ts +8 -0
  145. package/dist/config/stack.d.ts.map +1 -1
  146. package/dist/config/stack.js +1 -0
  147. package/dist/config/stack.js.map +1 -1
  148. package/dist/declared/injection/laid-out-calls.d.ts +7 -0
  149. package/dist/declared/injection/laid-out-calls.d.ts.map +1 -0
  150. package/dist/declared/injection/laid-out-calls.js +14 -0
  151. package/dist/declared/injection/laid-out-calls.js.map +1 -0
  152. package/dist/declared/render-barrel.d.ts.map +1 -1
  153. package/dist/declared/render-barrel.js +2 -1
  154. package/dist/declared/render-barrel.js.map +1 -1
  155. package/dist/declared/render-dependencies-file.d.ts.map +1 -1
  156. package/dist/declared/render-dependencies-file.js +2 -1
  157. package/dist/declared/render-dependencies-file.js.map +1 -1
  158. package/dist/generate/kind.d.ts +2 -0
  159. package/dist/generate/kind.d.ts.map +1 -1
  160. package/dist/generate/kinds/context-kind.d.ts.map +1 -1
  161. package/dist/generate/kinds/context-kind.js +13 -6
  162. package/dist/generate/kinds/context-kind.js.map +1 -1
  163. package/dist/generate/kinds/next-adr-number.d.ts +4 -0
  164. package/dist/generate/kinds/next-adr-number.d.ts.map +1 -0
  165. package/dist/generate/kinds/next-adr-number.js +7 -0
  166. package/dist/generate/kinds/next-adr-number.js.map +1 -0
  167. package/dist/generate/kinds/write-adr.js +3 -6
  168. package/dist/generate/kinds/write-adr.js.map +1 -1
  169. package/dist/help/usage.d.ts +1 -1
  170. package/dist/help/usage.d.ts.map +1 -1
  171. package/dist/help/usage.js +2 -2
  172. package/dist/init/manifest/versions.js +24 -24
  173. package/dist/init/run-init.d.ts +2 -0
  174. package/dist/init/run-init.d.ts.map +1 -1
  175. package/dist/init/run-init.js +15 -1
  176. package/dist/init/run-init.js.map +1 -1
  177. package/dist/init/stack/resolve-stack.d.ts.map +1 -1
  178. package/dist/init/stack/resolve-stack.js +3 -1
  179. package/dist/init/stack/resolve-stack.js.map +1 -1
  180. package/dist/init/stack/stack-options.d.ts +2 -0
  181. package/dist/init/stack/stack-options.d.ts.map +1 -1
  182. package/dist/presets/apply-preset.d.ts +15 -0
  183. package/dist/presets/apply-preset.d.ts.map +1 -0
  184. package/dist/presets/apply-preset.js +94 -0
  185. package/dist/presets/apply-preset.js.map +1 -0
  186. package/dist/presets/grant-to-signed-in.d.ts +9 -0
  187. package/dist/presets/grant-to-signed-in.d.ts.map +1 -0
  188. package/dist/presets/grant-to-signed-in.js +61 -0
  189. package/dist/presets/grant-to-signed-in.js.map +1 -0
  190. package/dist/presets/identity-preset.d.ts +9 -0
  191. package/dist/presets/identity-preset.d.ts.map +1 -0
  192. package/dist/presets/identity-preset.js +44 -0
  193. package/dist/presets/identity-preset.js.map +1 -0
  194. package/dist/presets/identity-refusal.d.ts +5 -0
  195. package/dist/presets/identity-refusal.d.ts.map +1 -0
  196. package/dist/presets/identity-refusal.js +29 -0
  197. package/dist/presets/identity-refusal.js.map +1 -0
  198. package/dist/presets/identity-terms.d.ts +4 -0
  199. package/dist/presets/identity-terms.d.ts.map +1 -0
  200. package/dist/presets/identity-terms.js +57 -0
  201. package/dist/presets/identity-terms.js.map +1 -0
  202. package/dist/presets/no-flags.d.ts +4 -0
  203. package/dist/presets/no-flags.d.ts.map +1 -0
  204. package/dist/presets/no-flags.js +18 -0
  205. package/dist/presets/no-flags.js.map +1 -0
  206. package/dist/presets/organisations-as-tenants.d.ts +9 -0
  207. package/dist/presets/organisations-as-tenants.d.ts.map +1 -0
  208. package/dist/presets/organisations-as-tenants.js +43 -0
  209. package/dist/presets/organisations-as-tenants.js.map +1 -0
  210. package/dist/presets/preset-decision.d.ts +8 -0
  211. package/dist/presets/preset-decision.d.ts.map +1 -0
  212. package/dist/presets/preset-decision.js +19 -0
  213. package/dist/presets/preset-decision.js.map +1 -0
  214. package/dist/presets/preset-file.d.ts +13 -0
  215. package/dist/presets/preset-file.d.ts.map +1 -0
  216. package/dist/presets/preset-file.js +2 -0
  217. package/dist/presets/preset-file.js.map +1 -0
  218. package/dist/presets/preset-files.d.ts +9 -0
  219. package/dist/presets/preset-files.d.ts.map +1 -0
  220. package/dist/presets/preset-files.js +34 -0
  221. package/dist/presets/preset-files.js.map +1 -0
  222. package/dist/presets/preset-named.d.ts +3 -0
  223. package/dist/presets/preset-named.d.ts.map +1 -0
  224. package/dist/presets/preset-named.js +12 -0
  225. package/dist/presets/preset-named.js.map +1 -0
  226. package/dist/presets/preset-refusal.d.ts +6 -0
  227. package/dist/presets/preset-refusal.d.ts.map +1 -0
  228. package/dist/presets/preset-refusal.js +2 -0
  229. package/dist/presets/preset-refusal.js.map +1 -0
  230. package/dist/presets/preset-step.d.ts +8 -0
  231. package/dist/presets/preset-step.d.ts.map +1 -0
  232. package/dist/presets/preset-step.js +2 -0
  233. package/dist/presets/preset-step.js.map +1 -0
  234. package/dist/presets/preset.d.ts +36 -0
  235. package/dist/presets/preset.d.ts.map +1 -0
  236. package/dist/presets/preset.js +2 -0
  237. package/dist/presets/preset.js.map +1 -0
  238. package/dist/presets/with-authorizer-import.d.ts +6 -0
  239. package/dist/presets/with-authorizer-import.d.ts.map +1 -0
  240. package/dist/presets/with-authorizer-import.js +27 -0
  241. package/dist/presets/with-authorizer-import.js.map +1 -0
  242. package/dist/stack/stack-vars.js +2 -1
  243. package/dist/stack/stack-vars.js.map +1 -1
  244. package/dist/version.d.ts +1 -1
  245. package/dist/version.js +1 -1
  246. package/dist/workspace/workspace.d.ts +2 -0
  247. package/dist/workspace/workspace.d.ts.map +1 -1
  248. package/dist/workspace/workspace.js +4 -0
  249. package/dist/workspace/workspace.js.map +1 -1
  250. package/package.json +2 -2
  251. package/templates/init/rules/domain.md.tpl +1 -1
  252. package/templates/pipeline/package-scripts.json +1 -1
  253. package/templates/presets/identity/context/application/authorization/__context__.permissions.ts.tpl +23 -0
  254. package/templates/presets/identity/context/application/authorization/change-member-roles.policy.ts.tpl +17 -0
  255. package/templates/presets/identity/context/application/authorization/grant-delegations.policy.ts.tpl +17 -0
  256. package/templates/presets/identity/context/application/authorization/invite-members.policy.ts.tpl +17 -0
  257. package/templates/presets/identity/context/application/authorization/read-members.policy.ts.tpl +17 -0
  258. package/templates/presets/identity/context/application/authorization/remove-members.policy.ts.tpl +17 -0
  259. package/templates/presets/identity/context/application/authorization/revoke-delegations.policy.ts.tpl +17 -0
  260. package/templates/presets/identity/context/application/authorization/revoke-invitations.policy.ts.tpl +17 -0
  261. package/templates/presets/identity/context/application/commands/accept-invitation/accept-invitation.command.ts.tpl +14 -0
  262. package/templates/presets/identity/context/application/commands/accept-invitation/accept-invitation.handler.ts.tpl +56 -0
  263. package/templates/presets/identity/context/application/commands/change-member-role/change-member-role.command.ts.tpl +17 -0
  264. package/templates/presets/identity/context/application/commands/change-member-role/change-member-role.handler.ts.tpl +82 -0
  265. package/templates/presets/identity/context/application/commands/deactivate-account/deactivate-account.command.ts.tpl +7 -0
  266. package/templates/presets/identity/context/application/commands/deactivate-account/deactivate-account.handler.ts.tpl +69 -0
  267. package/templates/presets/identity/context/application/commands/decline-invitation/decline-invitation.command.ts.tpl +14 -0
  268. package/templates/presets/identity/context/application/commands/decline-invitation/decline-invitation.handler.ts.tpl +36 -0
  269. package/templates/presets/identity/context/application/commands/grant-delegation/grant-delegation.command.ts.tpl +21 -0
  270. package/templates/presets/identity/context/application/commands/grant-delegation/grant-delegation.handler.ts.tpl +43 -0
  271. package/templates/presets/identity/context/application/commands/invite-member/invite-member.command.ts.tpl +16 -0
  272. package/templates/presets/identity/context/application/commands/invite-member/invite-member.handler.ts.tpl +53 -0
  273. package/templates/presets/identity/context/application/commands/open-organisation/open-organisation.command.ts.tpl +14 -0
  274. package/templates/presets/identity/context/application/commands/open-organisation/open-organisation.handler.ts.tpl +50 -0
  275. package/templates/presets/identity/context/application/commands/provision-account/provision-account.command.ts.tpl +14 -0
  276. package/templates/presets/identity/context/application/commands/provision-account/provision-account.handler.ts.tpl +41 -0
  277. package/templates/presets/identity/context/application/commands/remove-member/remove-member.command.ts.tpl +15 -0
  278. package/templates/presets/identity/context/application/commands/remove-member/remove-member.handler.ts.tpl +79 -0
  279. package/templates/presets/identity/context/application/commands/revoke-delegation/revoke-delegation.command.ts.tpl +15 -0
  280. package/templates/presets/identity/context/application/commands/revoke-delegation/revoke-delegation.handler.ts.tpl +39 -0
  281. package/templates/presets/identity/context/application/commands/revoke-invitation/revoke-invitation.command.ts.tpl +15 -0
  282. package/templates/presets/identity/context/application/commands/revoke-invitation/revoke-invitation.handler.ts.tpl +42 -0
  283. package/templates/presets/identity/context/application/integration/handlers/account-deactivated.integration-handler.ts.tpl +19 -0
  284. package/templates/presets/identity/context/application/integration/handlers/member-joined.integration-handler.ts.tpl +33 -0
  285. package/templates/presets/identity/context/application/integration/handlers/member-removed.integration-handler.ts.tpl +30 -0
  286. package/templates/presets/identity/context/application/integration/handlers/organisation-opened.integration-handler.ts.tpl +19 -0
  287. package/templates/presets/identity/context/application/integration/publishers/account-deactivated.publisher.ts.tpl +24 -0
  288. package/templates/presets/identity/context/application/integration/publishers/delegation-granted.publisher.ts.tpl +36 -0
  289. package/templates/presets/identity/context/application/integration/publishers/delegation-revoked.publisher.ts.tpl +33 -0
  290. package/templates/presets/identity/context/application/integration/publishers/member-joined.publisher.ts.tpl +33 -0
  291. package/templates/presets/identity/context/application/integration/publishers/member-removed.publisher.ts.tpl +33 -0
  292. package/templates/presets/identity/context/application/integration/publishers/member-role-changed.publisher.ts.tpl +35 -0
  293. package/templates/presets/identity/context/application/integration/publishers/organisation-opened.publisher.ts.tpl +32 -0
  294. package/templates/presets/identity/context/application/ports/identity-provider-admin.port.ts.tpl +17 -0
  295. package/templates/presets/identity/context/application/ports/identity-provider-connection.port.ts.tpl +14 -0
  296. package/templates/presets/identity/context/application/ports/invitation-tokens.port.ts.tpl +7 -0
  297. package/templates/presets/identity/context/application/ports/search-members-reader.port.ts.tpl +10 -0
  298. package/templates/presets/identity/context/application/queries/search-members/search-members.handler.ts.tpl +33 -0
  299. package/templates/presets/identity/context/application/queries/search-members/search-members.query.ts.tpl +19 -0
  300. package/templates/presets/identity/context/application/queries/search-members/search-members.result.ts.tpl +17 -0
  301. package/templates/presets/identity/context/domain/aggregates/account.aggregate.ts.tpl +92 -0
  302. package/templates/presets/identity/context/domain/aggregates/delegation.aggregate.ts.tpl +117 -0
  303. package/templates/presets/identity/context/domain/aggregates/invitation.aggregate.ts.tpl +128 -0
  304. package/templates/presets/identity/context/domain/aggregates/member.aggregate.ts.tpl +149 -0
  305. package/templates/presets/identity/context/domain/aggregates/organisation.aggregate.ts.tpl +52 -0
  306. package/templates/presets/identity/context/domain/errors/account.error.ts.tpl +40 -0
  307. package/templates/presets/identity/context/domain/errors/delegation.error.ts.tpl +45 -0
  308. package/templates/presets/identity/context/domain/errors/invitation.error.ts.tpl +30 -0
  309. package/templates/presets/identity/context/domain/errors/member.error.ts.tpl +50 -0
  310. package/templates/presets/identity/context/domain/errors/organisation.error.ts.tpl +32 -0
  311. package/templates/presets/identity/context/domain/events/account-deactivated.event.ts.tpl +13 -0
  312. package/templates/presets/identity/context/domain/events/account-provisioned.event.ts.tpl +17 -0
  313. package/templates/presets/identity/context/domain/events/delegation-granted.event.ts.tpl +22 -0
  314. package/templates/presets/identity/context/domain/events/delegation-revoked.event.ts.tpl +18 -0
  315. package/templates/presets/identity/context/domain/events/invitation-accepted.event.ts.tpl +17 -0
  316. package/templates/presets/identity/context/domain/events/invitation-declined.event.ts.tpl +16 -0
  317. package/templates/presets/identity/context/domain/events/invitation-issued.event.ts.tpl +22 -0
  318. package/templates/presets/identity/context/domain/events/invitation-revoked.event.ts.tpl +16 -0
  319. package/templates/presets/identity/context/domain/events/member-joined.event.ts.tpl +19 -0
  320. package/templates/presets/identity/context/domain/events/member-removed.event.ts.tpl +19 -0
  321. package/templates/presets/identity/context/domain/events/member-role-changed.event.ts.tpl +22 -0
  322. package/templates/presets/identity/context/domain/events/organisation-opened.event.ts.tpl +17 -0
  323. package/templates/presets/identity/context/domain/repositories/account.repository.ts.tpl +5 -0
  324. package/templates/presets/identity/context/domain/repositories/delegation.repository.ts.tpl +10 -0
  325. package/templates/presets/identity/context/domain/repositories/invitation.repository.ts.tpl +7 -0
  326. package/templates/presets/identity/context/domain/repositories/member.repository.ts.tpl +13 -0
  327. package/templates/presets/identity/context/domain/repositories/organisation.repository.ts.tpl +5 -0
  328. package/templates/presets/identity/context/domain/services/role-rights.service.ts.tpl +59 -0
  329. package/templates/presets/identity/context/domain/value-objects/delegation-period.vo.ts.tpl +42 -0
  330. package/templates/presets/identity/context/domain/value-objects/display-name.vo.ts.tpl +29 -0
  331. package/templates/presets/identity/context/domain/value-objects/email-address.vo.ts.tpl +31 -0
  332. package/templates/presets/identity/context/domain/value-objects/organisation-name.vo.ts.tpl +29 -0
  333. package/templates/presets/identity/context/domain/value-objects/organisation-rights.vo.ts.tpl +43 -0
  334. package/templates/presets/identity/context/domain/value-objects/organisation-slug.vo.ts.tpl +30 -0
  335. package/templates/presets/identity/context/domain/vocabularies/account-status.vocabulary.ts.tpl +7 -0
  336. package/templates/presets/identity/context/domain/vocabularies/invitation-status.vocabulary.ts.tpl +9 -0
  337. package/templates/presets/identity/context/domain/vocabularies/member-right.vocabulary.ts.tpl +9 -0
  338. package/templates/presets/identity/context/domain/vocabularies/member-role.vocabulary.ts.tpl +8 -0
  339. package/templates/presets/identity/context/infrastructure/external/crypto-invitation-tokens.adapter.ts.tpl +20 -0
  340. package/templates/presets/identity/context/infrastructure/external/environment-identity-provider-connection.adapter.ts.tpl +37 -0
  341. package/templates/presets/identity/context/infrastructure/external/keycloak-identity-provider-admin.adapter.ts.tpl +149 -0
  342. package/templates/presets/identity/context/infrastructure/persistence/__context__.schema.drizzle.ts.tpl +102 -0
  343. package/templates/presets/identity/context/infrastructure/persistence/account-row.schema.knex.ts.tpl +15 -0
  344. package/templates/presets/identity/context/infrastructure/persistence/account.mapper.drizzle.ts.tpl +38 -0
  345. package/templates/presets/identity/context/infrastructure/persistence/account.mapper.knex.ts.tpl +33 -0
  346. package/templates/presets/identity/context/infrastructure/persistence/create-__context__-tables.schema.knex.ts.tpl +107 -0
  347. package/templates/presets/identity/context/infrastructure/persistence/delegation-row.schema.knex.ts.tpl +17 -0
  348. package/templates/presets/identity/context/infrastructure/persistence/delegation.mapper.drizzle.ts.tpl +50 -0
  349. package/templates/presets/identity/context/infrastructure/persistence/delegation.mapper.knex.ts.tpl +45 -0
  350. package/templates/presets/identity/context/infrastructure/persistence/drizzle-account.repository.drizzle.ts.tpl +59 -0
  351. package/templates/presets/identity/context/infrastructure/persistence/drizzle-delegation.repository.drizzle.ts.tpl +77 -0
  352. package/templates/presets/identity/context/infrastructure/persistence/drizzle-invitation.repository.drizzle.ts.tpl +65 -0
  353. package/templates/presets/identity/context/infrastructure/persistence/drizzle-member.repository.drizzle.ts.tpl +84 -0
  354. package/templates/presets/identity/context/infrastructure/persistence/drizzle-organisation.repository.drizzle.ts.tpl +59 -0
  355. package/templates/presets/identity/context/infrastructure/persistence/drizzle-search-members-reader.repository.drizzle.ts.tpl +53 -0
  356. package/templates/presets/identity/context/infrastructure/persistence/invitation-row.schema.knex.ts.tpl +20 -0
  357. package/templates/presets/identity/context/infrastructure/persistence/invitation.mapper.drizzle.ts.tpl +45 -0
  358. package/templates/presets/identity/context/infrastructure/persistence/invitation.mapper.knex.ts.tpl +40 -0
  359. package/templates/presets/identity/context/infrastructure/persistence/knex-account.repository.knex.ts.tpl +61 -0
  360. package/templates/presets/identity/context/infrastructure/persistence/knex-delegation.repository.knex.ts.tpl +80 -0
  361. package/templates/presets/identity/context/infrastructure/persistence/knex-invitation.repository.knex.ts.tpl +67 -0
  362. package/templates/presets/identity/context/infrastructure/persistence/knex-member.repository.knex.ts.tpl +82 -0
  363. package/templates/presets/identity/context/infrastructure/persistence/knex-organisation.repository.knex.ts.tpl +61 -0
  364. package/templates/presets/identity/context/infrastructure/persistence/knex-search-members-reader.repository.knex.ts.tpl +77 -0
  365. package/templates/presets/identity/context/infrastructure/persistence/member-row.schema.knex.ts.tpl +15 -0
  366. package/templates/presets/identity/context/infrastructure/persistence/member.mapper.drizzle.ts.tpl +36 -0
  367. package/templates/presets/identity/context/infrastructure/persistence/member.mapper.knex.ts.tpl +31 -0
  368. package/templates/presets/identity/context/infrastructure/persistence/organisation-row.schema.knex.ts.tpl +12 -0
  369. package/templates/presets/identity/context/infrastructure/persistence/organisation.mapper.drizzle.ts.tpl +28 -0
  370. package/templates/presets/identity/context/infrastructure/persistence/organisation.mapper.knex.ts.tpl +26 -0
  371. package/templates/presets/identity/context/presentation/http/accounts/deactivate-account/deactivate-account.accepted.class-validator.per-route.ts.tpl +6 -0
  372. package/templates/presets/identity/context/presentation/http/accounts/deactivate-account/deactivate-account.controller.ts.tpl +71 -0
  373. package/templates/presets/identity/context/presentation/http/accounts/provision-account/provision-account.accepted.class-validator.per-route.ts.tpl +6 -0
  374. package/templates/presets/identity/context/presentation/http/accounts/provision-account/provision-account.body.class-validator.ts.tpl +9 -0
  375. package/templates/presets/identity/context/presentation/http/accounts/provision-account/provision-account.controller.ts.tpl +85 -0
  376. package/templates/presets/identity/context/presentation/http/accounts.routes.ts.tpl +5 -0
  377. package/templates/presets/identity/context/presentation/http/invitations/accept-invitation/accept-invitation.accepted.class-validator.per-route.ts.tpl +6 -0
  378. package/templates/presets/identity/context/presentation/http/invitations/accept-invitation/accept-invitation.body.class-validator.per-route.ts.tpl +8 -0
  379. package/templates/presets/identity/context/presentation/http/invitations/accept-invitation/accept-invitation.controller.ts.tpl +100 -0
  380. package/templates/presets/identity/context/presentation/http/invitations/decline-invitation/decline-invitation.accepted.class-validator.per-route.ts.tpl +6 -0
  381. package/templates/presets/identity/context/presentation/http/invitations/decline-invitation/decline-invitation.body.class-validator.per-route.ts.tpl +8 -0
  382. package/templates/presets/identity/context/presentation/http/invitations/decline-invitation/decline-invitation.controller.ts.tpl +98 -0
  383. package/templates/presets/identity/context/presentation/http/invitations.routes.ts.tpl +5 -0
  384. package/templates/presets/identity/context/presentation/http/organisations/change-member-role/change-member-role.accepted.class-validator.per-route.ts.tpl +6 -0
  385. package/templates/presets/identity/context/presentation/http/organisations/change-member-role/change-member-role.body.class-validator.ts.tpl +7 -0
  386. package/templates/presets/identity/context/presentation/http/organisations/change-member-role/change-member-role.controller.ts.tpl +83 -0
  387. package/templates/presets/identity/context/presentation/http/organisations/change-member-role/change-member-role.params.class-validator.ts.tpl +11 -0
  388. package/templates/presets/identity/context/presentation/http/organisations/grant-delegation/grant-delegation.accepted.class-validator.per-route.ts.tpl +6 -0
  389. package/templates/presets/identity/context/presentation/http/organisations/grant-delegation/grant-delegation.body.class-validator.ts.tpl +18 -0
  390. package/templates/presets/identity/context/presentation/http/organisations/grant-delegation/grant-delegation.controller.ts.tpl +103 -0
  391. package/templates/presets/identity/context/presentation/http/organisations/grant-delegation/grant-delegation.params.class-validator.ts.tpl +7 -0
  392. package/templates/presets/identity/context/presentation/http/organisations/invite-member/invite-member.accepted.class-validator.ts.tpl +10 -0
  393. package/templates/presets/identity/context/presentation/http/organisations/invite-member/invite-member.body.class-validator.ts.tpl +10 -0
  394. package/templates/presets/identity/context/presentation/http/organisations/invite-member/invite-member.controller.ts.tpl +65 -0
  395. package/templates/presets/identity/context/presentation/http/organisations/invite-member/invite-member.params.class-validator.ts.tpl +7 -0
  396. package/templates/presets/identity/context/presentation/http/organisations/open-organisation/open-organisation.accepted.class-validator.per-route.ts.tpl +6 -0
  397. package/templates/presets/identity/context/presentation/http/organisations/open-organisation/open-organisation.body.class-validator.ts.tpl +12 -0
  398. package/templates/presets/identity/context/presentation/http/organisations/open-organisation/open-organisation.controller.ts.tpl +79 -0
  399. package/templates/presets/identity/context/presentation/http/organisations/remove-member/remove-member.accepted.class-validator.per-route.ts.tpl +6 -0
  400. package/templates/presets/identity/context/presentation/http/organisations/remove-member/remove-member.controller.ts.tpl +73 -0
  401. package/templates/presets/identity/context/presentation/http/organisations/remove-member/remove-member.params.class-validator.ts.tpl +11 -0
  402. package/templates/presets/identity/context/presentation/http/organisations/revoke-delegation/revoke-delegation.accepted.class-validator.per-route.ts.tpl +6 -0
  403. package/templates/presets/identity/context/presentation/http/organisations/revoke-delegation/revoke-delegation.controller.ts.tpl +73 -0
  404. package/templates/presets/identity/context/presentation/http/organisations/revoke-delegation/revoke-delegation.params.class-validator.ts.tpl +11 -0
  405. package/templates/presets/identity/context/presentation/http/organisations/revoke-invitation/revoke-invitation.accepted.class-validator.per-route.ts.tpl +6 -0
  406. package/templates/presets/identity/context/presentation/http/organisations/revoke-invitation/revoke-invitation.controller.ts.tpl +73 -0
  407. package/templates/presets/identity/context/presentation/http/organisations/revoke-invitation/revoke-invitation.params.class-validator.ts.tpl +11 -0
  408. package/templates/presets/identity/context/presentation/http/organisations/search-members/search-members.controller.ts.tpl +86 -0
  409. package/templates/presets/identity/context/presentation/http/organisations/search-members/search-members.line.class-validator.ts.tpl +22 -0
  410. package/templates/presets/identity/context/presentation/http/organisations/search-members/search-members.params.class-validator.ts.tpl +7 -0
  411. package/templates/presets/identity/context/presentation/http/organisations.routes.ts.tpl +5 -0
  412. package/templates/presets/identity/context/presentation/http/shared/invitation-token.schema.class-validator.shared.ts.tpl +9 -0
  413. package/templates/presets/identity/context/presentation/http/shared/invitation-token.schema.zod.shared.ts.tpl +9 -0
  414. package/templates/presets/identity/context/published-language/account-deactivated.v1.integration-event.ts.tpl +33 -0
  415. package/templates/presets/identity/context/published-language/account-deactivated.v1.payload.class-validator.ts.tpl +7 -0
  416. package/templates/presets/identity/context/published-language/delegation-granted.v1.integration-event.ts.tpl +39 -0
  417. package/templates/presets/identity/context/published-language/delegation-granted.v1.payload.class-validator.ts.tpl +28 -0
  418. package/templates/presets/identity/context/published-language/delegation-revoked.v1.integration-event.ts.tpl +36 -0
  419. package/templates/presets/identity/context/published-language/delegation-revoked.v1.payload.class-validator.ts.tpl +16 -0
  420. package/templates/presets/identity/context/published-language/member-joined.v1.integration-event.ts.tpl +36 -0
  421. package/templates/presets/identity/context/published-language/member-joined.v1.payload.class-validator.ts.tpl +16 -0
  422. package/templates/presets/identity/context/published-language/member-removed.v1.integration-event.ts.tpl +36 -0
  423. package/templates/presets/identity/context/published-language/member-removed.v1.payload.class-validator.ts.tpl +18 -0
  424. package/templates/presets/identity/context/published-language/member-role-changed.v1.integration-event.ts.tpl +38 -0
  425. package/templates/presets/identity/context/published-language/member-role-changed.v1.payload.class-validator.ts.tpl +24 -0
  426. package/templates/presets/identity/context/published-language/organisation-opened.v1.integration-event.ts.tpl +35 -0
  427. package/templates/presets/identity/context/published-language/organisation-opened.v1.payload.class-validator.ts.tpl +13 -0
  428. package/templates/presets/identity/context/test/builders/account.builder.ts.tpl +59 -0
  429. package/templates/presets/identity/context/test/builders/delegation.builder.ts.tpl +67 -0
  430. package/templates/presets/identity/context/test/builders/invitation.builder.ts.tpl +60 -0
  431. package/templates/presets/identity/context/test/builders/member.builder.ts.tpl +59 -0
  432. package/templates/presets/identity/context/test/builders/organisation.builder.ts.tpl +44 -0
  433. package/templates/presets/identity/context/test/fakes/account.repository.fake.ts.tpl +10 -0
  434. package/templates/presets/identity/context/test/fakes/delegation.repository.fake.ts.tpl +26 -0
  435. package/templates/presets/identity/context/test/fakes/identity-provider-admin.fake.ts.tpl +41 -0
  436. package/templates/presets/identity/context/test/fakes/invitation-tokens.fake.ts.tpl +18 -0
  437. package/templates/presets/identity/context/test/fakes/invitation.repository.fake.ts.tpl +14 -0
  438. package/templates/presets/identity/context/test/fakes/member.repository.fake.ts.tpl +29 -0
  439. package/templates/presets/identity/context/test/fakes/organisation.repository.fake.ts.tpl +10 -0
  440. package/templates/presets/identity/context/test/fakes/search-members-reader.fake.ts.tpl +25 -0
  441. package/templates/presets/identity/context/test/integration/account.repository.contract.spec.ts.tpl +48 -0
  442. package/templates/presets/identity/context/test/integration/delegation.repository.contract.spec.ts.tpl +84 -0
  443. package/templates/presets/identity/context/test/integration/invitation.repository.contract.spec.ts.tpl +76 -0
  444. package/templates/presets/identity/context/test/integration/keycloak-identity-provider-admin.adapter.spec.ts.tpl +123 -0
  445. package/templates/presets/identity/context/test/integration/member.repository.contract.spec.ts.tpl +92 -0
  446. package/templates/presets/identity/context/test/integration/organisation.repository.contract.spec.ts.tpl +46 -0
  447. package/templates/presets/identity/context/test/integration/search-members-reader.spec.ts.tpl +86 -0
  448. package/templates/presets/identity/context/test/unit/accept-invitation.controller.spec.ts.tpl +57 -0
  449. package/templates/presets/identity/context/test/unit/accept-invitation.handler.spec.ts.tpl +69 -0
  450. package/templates/presets/identity/context/test/unit/account-deactivated.integration-handler.spec.ts.tpl +24 -0
  451. package/templates/presets/identity/context/test/unit/account-deactivated.publisher.spec.ts.tpl +25 -0
  452. package/templates/presets/identity/context/test/unit/account.aggregate.spec.ts.tpl +72 -0
  453. package/templates/presets/identity/context/test/unit/change-member-role.controller.spec.ts.tpl +57 -0
  454. package/templates/presets/identity/context/test/unit/change-member-role.handler.spec.ts.tpl +135 -0
  455. package/templates/presets/identity/context/test/unit/crypto-invitation-tokens.adapter.spec.ts.tpl +15 -0
  456. package/templates/presets/identity/context/test/unit/deactivate-account.controller.spec.ts.tpl +44 -0
  457. package/templates/presets/identity/context/test/unit/deactivate-account.handler.spec.ts.tpl +106 -0
  458. package/templates/presets/identity/context/test/unit/decline-invitation.controller.spec.ts.tpl +57 -0
  459. package/templates/presets/identity/context/test/unit/decline-invitation.handler.spec.ts.tpl +40 -0
  460. package/templates/presets/identity/context/test/unit/delegation-granted.publisher.spec.ts.tpl +36 -0
  461. package/templates/presets/identity/context/test/unit/delegation-period.vo.spec.ts.tpl +32 -0
  462. package/templates/presets/identity/context/test/unit/delegation-revoked.publisher.spec.ts.tpl +35 -0
  463. package/templates/presets/identity/context/test/unit/delegation.aggregate.spec.ts.tpl +111 -0
  464. package/templates/presets/identity/context/test/unit/display-name.vo.spec.ts.tpl +13 -0
  465. package/templates/presets/identity/context/test/unit/email-address.vo.spec.ts.tpl +24 -0
  466. package/templates/presets/identity/context/test/unit/environment-identity-provider-connection.adapter.spec.ts.tpl +41 -0
  467. package/templates/presets/identity/context/test/unit/grant-delegation.controller.spec.ts.tpl +57 -0
  468. package/templates/presets/identity/context/test/unit/grant-delegation.handler.spec.ts.tpl +66 -0
  469. package/templates/presets/identity/context/test/unit/identity-application.ts.tpl +79 -0
  470. package/templates/presets/identity/context/test/unit/invitation.aggregate.spec.ts.tpl +118 -0
  471. package/templates/presets/identity/context/test/unit/invite-member.controller.spec.ts.tpl +57 -0
  472. package/templates/presets/identity/context/test/unit/invite-member.handler.spec.ts.tpl +110 -0
  473. package/templates/presets/identity/context/test/unit/member-joined.integration-handler.spec.ts.tpl +38 -0
  474. package/templates/presets/identity/context/test/unit/member-joined.publisher.spec.ts.tpl +36 -0
  475. package/templates/presets/identity/context/test/unit/member-removed.integration-handler.spec.ts.tpl +45 -0
  476. package/templates/presets/identity/context/test/unit/member-removed.publisher.spec.ts.tpl +35 -0
  477. package/templates/presets/identity/context/test/unit/member-role-changed.publisher.spec.ts.tpl +36 -0
  478. package/templates/presets/identity/context/test/unit/member.aggregate.spec.ts.tpl +142 -0
  479. package/templates/presets/identity/context/test/unit/open-organisation.controller.spec.ts.tpl +57 -0
  480. package/templates/presets/identity/context/test/unit/open-organisation.handler.spec.ts.tpl +52 -0
  481. package/templates/presets/identity/context/test/unit/organisation-name.vo.spec.ts.tpl +13 -0
  482. package/templates/presets/identity/context/test/unit/organisation-opened.integration-handler.spec.ts.tpl +24 -0
  483. package/templates/presets/identity/context/test/unit/organisation-opened.publisher.spec.ts.tpl +35 -0
  484. package/templates/presets/identity/context/test/unit/organisation-rights.vo.spec.ts.tpl +23 -0
  485. package/templates/presets/identity/context/test/unit/organisation-slug.vo.spec.ts.tpl +17 -0
  486. package/templates/presets/identity/context/test/unit/organisation.aggregate.spec.ts.tpl +33 -0
  487. package/templates/presets/identity/context/test/unit/provision-account.controller.spec.ts.tpl +73 -0
  488. package/templates/presets/identity/context/test/unit/provision-account.handler.spec.ts.tpl +65 -0
  489. package/templates/presets/identity/context/test/unit/remove-member.controller.spec.ts.tpl +44 -0
  490. package/templates/presets/identity/context/test/unit/remove-member.handler.spec.ts.tpl +125 -0
  491. package/templates/presets/identity/context/test/unit/revoke-delegation.controller.spec.ts.tpl +44 -0
  492. package/templates/presets/identity/context/test/unit/revoke-delegation.handler.spec.ts.tpl +51 -0
  493. package/templates/presets/identity/context/test/unit/revoke-invitation.controller.spec.ts.tpl +44 -0
  494. package/templates/presets/identity/context/test/unit/revoke-invitation.handler.spec.ts.tpl +44 -0
  495. package/templates/presets/identity/context/test/unit/role-rights.service.spec.ts.tpl +54 -0
  496. package/templates/presets/identity/context/test/unit/search-members.controller.spec.ts.tpl +43 -0
  497. package/templates/presets/identity/context/test/unit/search-members.handler.spec.ts.tpl +38 -0
  498. package/templates/presets/identity/decision.md.tpl +37 -0
  499. package/templates/presets/identity/design.md.tpl +71 -0
  500. package/templates/presets/signed-in-grants.ts.tpl +12 -0
@@ -10,7 +10,7 @@ paths:
10
10
  - Names come from the glossary of `CONTEXT.md`, written first (`language/glossary-term`); no `Manager`, `Helper`, `Data`, `Info`, `Model` (`language/no-technical-name`).
11
11
  - Aggregate: private constructor, `create` for birth, `rehydrate` for loading, state changed by methods named after the business intention, no setter, no public mutable property (`domain/aggregate-encapsulation`). One aggregate per transaction; another one is referenced by its id.
12
12
  - A status that moves through a lifecycle is declared once as a state machine, `state-machines/<aggregate>.state-machine.ts` (`generate state-machine <context> <Aggregate> --value .. --transition ..`): the aggregate writes its status only from `machine.next(..)` or `machine.start(..)` (`lifecycle/status-through-machine`), and a forbidden transition is refused by a static factory of its error class (`refused`).
13
- - Value objects are immutable, validated in `create`, compared with `equals` (`domain/value-object-immutability`). An email, an amount, a period are types, not strings.
13
+ - A value object is a class extending `ValueObject` of the kernel, one per `value-objects/*.vo.ts` (`npx ddd-ts-kit generate value-object <context> <Name>`): a private constructor, static factories that check its rules (`create`, or named after what they build: `of`, `between`), its operations as methods that return a new instance. It is immutable and compared with `equals` (`domain/value-object-immutability`). An email, an amount, a period are types, not strings.{{#if values.classes}} A value is never a function that builds a plain object, nor an exported object literal (`domain/value-object-class`): the class compares by value, checks its invariants once and is recognised with `instanceof` by a mapper. A function of `domain/` or of the shared kernel that answers a record of data, `({ amount, currency })`, is reported; one that answers value objects it builds (`Money.of(..)`), derives a record with a spread, or answers functions (a specification) is not, and a decision is a refusal thrown by a factory or a value object.{{/if}}
14
14
  {{#if literals.named}}
15
15
  - A closed list of values (a status, a kind, a role of the model) is a vocabulary, `npx ddd-ts-kit generate vocabulary <context> <Name> --value <value>...`: code reads `AppointmentStatus.NoShow`, never the string (`language/declared-vocabulary`).
16
16
  - No number without a name: 0, 1, -1 and an index only; a duration, a limit or a rate is a constant of the module (`const MINUTES_PER_HOUR = 60;`) or a value object (`clean/no-magic-number`).
@@ -23,7 +23,7 @@
23
23
  "@biomejs/biome": "2.5.15",
24
24
  "@commitlint/cli": "^21.2.3",
25
25
  "@commitlint/config-conventional": "^21.2.3",
26
- "@ddd-ts-kit/eslint-plugin": "^0.14.0",
26
+ "@ddd-ts-kit/eslint-plugin": "^0.16.0",
27
27
  "@eslint/js": "^10.0.1",
28
28
  "@typescript/native": "npm:typescript@~7.0.2",
29
29
  "@vitest/coverage-v8": "5.0.3",
@@ -0,0 +1,23 @@
1
+ import { definePermissions } from '@ddd-ts-kit/auth';
2
+
3
+ /**
4
+ * What a use case of {{context.kebab}} requires. The platform grants them to every signed-in person;
5
+ * inside an organisation, the policies of this context then decide from the rights of the actor's
6
+ * role and of the delegations lent to them.
7
+ */
8
+ export const {{context.camel}}Permissions = definePermissions('{{context.kebab}}', {
9
+ // ddd-ts-kit:permissions
10
+ 'account.provision': { description: 'Provision or refresh the account of the person who signs in' },
11
+ 'account.deactivate': { description: 'Deactivate the account of the person who asks' },
12
+ 'organisation.open': { description: 'Open an organisation and become its first owner' },
13
+ 'member.invite': { description: 'Invite a person to an organisation, in a role' },
14
+ 'invitation.accept': { description: 'Accept an invitation addressed to oneself' },
15
+ 'invitation.decline': { description: 'Decline an invitation addressed to oneself' },
16
+ 'invitation.revoke': { description: 'Take back an invitation of an organisation' },
17
+ 'member.change-role': { description: 'Change the role of a member of an organisation' },
18
+ 'member.remove': { description: 'Remove a member from an organisation' },
19
+ 'member.search': { description: 'Search the members of an organisation' },
20
+ 'delegation.grant': { description: 'Lend some of the rights of a role to another member, for a period' },
21
+ 'delegation.revoke': { description: 'Revoke a delegation of an organisation' },
22
+ // /ddd-ts-kit:permissions
23
+ });
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account changes the roles of an organisation where it manages the members. */
8
+ @Policy({{context.camel}}Permissions['member.change-role'], { name: '{{context.kebab}}.change-member-roles' })
9
+ export class ChangeMemberRolesPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.ManageMembers);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account lends rights in an organisation where its role lets it delegate. */
8
+ @Policy({{context.camel}}Permissions['delegation.grant'], { name: '{{context.kebab}}.grant-delegations' })
9
+ export class GrantDelegationsPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.GrantDelegations);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account invites to an organisation when its role, or a delegation, lets it invite there. */
8
+ @Policy({{context.camel}}Permissions['member.invite'], { name: '{{context.kebab}}.invite-members' })
9
+ export class InviteMembersPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.InviteMembers);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account searches the members of an organisation it belongs to. */
8
+ @Policy({{context.camel}}Permissions['member.search'], { name: '{{context.kebab}}.read-members' })
9
+ export class ReadMembersPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.ReadMembers);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account removes members of an organisation where it manages the members. */
8
+ @Policy({{context.camel}}Permissions['member.remove'], { name: '{{context.kebab}}.remove-members' })
9
+ export class RemoveMembersPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.ManageMembers);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account revokes the delegations of an organisation where its role lets it delegate. */
8
+ @Policy({{context.camel}}Permissions['delegation.revoke'], { name: '{{context.kebab}}.revoke-delegations' })
9
+ export class RevokeDelegationsPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.GrantDelegations);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Policy } from '@ddd-ts-kit/kernel/declarations';
3
+ import { OrganisationRights } from '{{alias.context}}/domain/value-objects/organisation-rights.vo{{alias.ext}}';
4
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
6
+
7
+ /** An account takes back the invitations of an organisation where it may invite. */
8
+ @Policy({{context.camel}}Permissions['invitation.revoke'], { name: '{{context.kebab}}.revoke-invitations' })
9
+ export class RevokeInvitationsPolicy {
10
+ appliesTo(subject: unknown): subject is OrganisationRights {
11
+ return subject instanceof OrganisationRights;
12
+ }
13
+
14
+ allows(actor: Actor, rights: OrganisationRights): boolean {
15
+ return isAuthenticated(actor) && rights.accountId === actor.id && rights.includes(MemberRight.InviteMembers);
16
+ }
17
+ }
@@ -0,0 +1,14 @@
1
+ import { AuthorizedCommand, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+
5
+ /** Accepts the invitation of this token, addressed to the actor: the actor becomes a member. */
6
+ @Requires({{context.camel}}Permissions['invitation.accept'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
7
+ export class AcceptInvitationCommand extends AuthorizedCommand<string> {
8
+ constructor(
9
+ actor: Actor,
10
+ readonly token: string,
11
+ ) {
12
+ super(actor);
13
+ }
14
+ }
@@ -0,0 +1,56 @@
1
+ import type { Clock, IdGenerator } from '@ddd-ts-kit/kernel';
2
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
3
+ import { Member } from '{{alias.context}}/domain/aggregates/member.aggregate{{alias.ext}}';
4
+ import { AccountError } from '{{alias.context}}/domain/errors/account.error{{alias.ext}}';
5
+ import { InvitationError } from '{{alias.context}}/domain/errors/invitation.error{{alias.ext}}';
6
+ import { MemberError } from '{{alias.context}}/domain/errors/member.error{{alias.ext}}';
7
+ import type { AccountRepository } from '{{alias.context}}/domain/repositories/account.repository{{alias.ext}}';
8
+ import type { InvitationRepository } from '{{alias.context}}/domain/repositories/invitation.repository{{alias.ext}}';
9
+ import type { MemberRepository } from '{{alias.context}}/domain/repositories/member.repository{{alias.ext}}';
10
+ import { AcceptInvitationCommand } from '{{alias.context}}/application/commands/accept-invitation/accept-invitation.command{{alias.ext}}';
11
+ import type { InvitationTokens } from '{{alias.context}}/application/ports/invitation-tokens.port{{alias.ext}}';
12
+
13
+ @CommandHandler(AcceptInvitationCommand)
14
+ export class AcceptInvitationHandler implements CommandHandler<AcceptInvitationCommand> {
15
+ constructor(
16
+ private readonly invitationRepository: InvitationRepository,
17
+ private readonly memberRepository: MemberRepository,
18
+ private readonly accountRepository: AccountRepository,
19
+ private readonly tokens: InvitationTokens,
20
+ private readonly ids: IdGenerator,
21
+ private readonly clock: Clock,
22
+ ) {}
23
+
24
+ /** The person the invitation was sent to, signed in, answers with its token: they join in the role it names. */
25
+ async execute({ actor, token }: AcceptInvitationCommand): Promise<string> {
26
+ const invitation = await this.invitationRepository.findByTokenHash(this.tokens.digestOf(token));
27
+ const account = await this.accountRepository.findById(actor.id);
28
+
29
+ if (invitation === undefined) {
30
+ throw InvitationError.notFound();
31
+ }
32
+
33
+ if (account === undefined) {
34
+ throw AccountError.notFound(actor.id);
35
+ }
36
+
37
+ account.assertActive();
38
+
39
+ const now = this.clock.now();
40
+ const { organisationId, role } = invitation;
41
+
42
+ invitation.accept(account, now);
43
+
44
+ if ((await this.memberRepository.findOf(organisationId, account.id)) !== undefined) {
45
+ throw MemberError.alreadyMember(organisationId, account.id);
46
+ }
47
+
48
+ await this.invitationRepository.save(invitation);
49
+
50
+ await this.memberRepository.save(
51
+ Member.join(this.ids.next(), { organisationId, accountId: account.id, role }, now),
52
+ );
53
+
54
+ return invitation.id;
55
+ }
56
+ }
@@ -0,0 +1,17 @@
1
+ import { AuthorizedCommand, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+ import type { MemberRole } from '{{alias.context}}/domain/vocabularies/member-role.vocabulary{{alias.ext}}';
5
+
6
+ /** Changes the role of a member of an organisation. */
7
+ @Requires({{context.camel}}Permissions['member.change-role'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
8
+ export class ChangeMemberRoleCommand extends AuthorizedCommand<string> {
9
+ constructor(
10
+ actor: Actor,
11
+ readonly organisationId: string,
12
+ readonly accountId: string,
13
+ readonly role: MemberRole,
14
+ ) {
15
+ super(actor);
16
+ }
17
+ }
@@ -0,0 +1,82 @@
1
+ import type { Access } from '@ddd-ts-kit/auth';
2
+ import type { Clock } from '@ddd-ts-kit/kernel';
3
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
4
+ import type { Member } from '{{alias.context}}/domain/aggregates/member.aggregate{{alias.ext}}';
5
+ import type { Organisation } from '{{alias.context}}/domain/aggregates/organisation.aggregate{{alias.ext}}';
6
+ import { MemberError } from '{{alias.context}}/domain/errors/member.error{{alias.ext}}';
7
+ import { OrganisationError } from '{{alias.context}}/domain/errors/organisation.error{{alias.ext}}';
8
+ import type { DelegationRepository } from '{{alias.context}}/domain/repositories/delegation.repository{{alias.ext}}';
9
+ import type { MemberRepository } from '{{alias.context}}/domain/repositories/member.repository{{alias.ext}}';
10
+ import type { OrganisationRepository } from '{{alias.context}}/domain/repositories/organisation.repository{{alias.ext}}';
11
+ import { RoleRightsService, type HeldRights } from '{{alias.context}}/domain/services/role-rights.service{{alias.ext}}';
12
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
13
+ import { MemberRole } from '{{alias.context}}/domain/vocabularies/member-role.vocabulary{{alias.ext}}';
14
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
15
+ import { ChangeMemberRoleCommand } from '{{alias.context}}/application/commands/change-member-role/change-member-role.command{{alias.ext}}';
16
+
17
+ @CommandHandler(ChangeMemberRoleCommand)
18
+ export class ChangeMemberRoleHandler implements CommandHandler<ChangeMemberRoleCommand> {
19
+ constructor(
20
+ private readonly organisationRepository: OrganisationRepository,
21
+ private readonly memberRepository: MemberRepository,
22
+ private readonly delegationRepository: DelegationRepository,
23
+ private readonly access: Access,
24
+ private readonly clock: Clock,
25
+ ) {}
26
+
27
+ /**
28
+ * Within the role of who decides, never on their own role. The organisation is saved with the
29
+ * change, unchanged, so that its version orders the changes of its owners: two owners who
30
+ * demote each other at once cannot both commit and leave it without one. The delegations the
31
+ * member gave end with the role they were granted under: what they lent may no longer be theirs.
32
+ */
33
+ async execute({ actor, organisationId, accountId, role }: ChangeMemberRoleCommand): Promise<string> {
34
+ const now = this.clock.now();
35
+ const held = await this.heldBy(organisationId, actor.id, now);
36
+ const rights = new RoleRightsService();
37
+
38
+ await this.access.authorize(actor, {{context.camel}}Permissions['member.change-role'], rights.rightsIn(organisationId, actor.id, held));
39
+
40
+ const { organisation, member } = await this.membershipIn(organisationId, accountId);
41
+ const delegationId = rights.delegationUsedFor(MemberRight.ManageMembers, held);
42
+ const owners = await this.memberRepository.countOwners(organisationId);
43
+
44
+ member.changeRole(role, { by: held.member?.role ?? MemberRole.Member, byAccount: actor.id, delegationId, owners }, now);
45
+ await this.organisationRepository.save(organisation);
46
+ await this.memberRepository.save(member);
47
+ await this.endDelegationsGivenBy(organisationId, accountId, now);
48
+
49
+ return member.id;
50
+ }
51
+
52
+ private async heldBy(organisationId: string, accountId: string, now: Date): Promise<HeldRights> {
53
+ const member = await this.memberRepository.findOf(organisationId, accountId);
54
+
55
+ return { member, lent: await this.delegationRepository.findUnendedTo(organisationId, accountId), now };
56
+ }
57
+
58
+ /** The organisation, to be saved with the change, and the membership the change is about. */
59
+ private async membershipIn(organisationId: string, accountId: string): Promise<{ organisation: Organisation; member: Member }> {
60
+ const organisation = await this.organisationRepository.findById(organisationId);
61
+ const member = await this.memberRepository.findOf(organisationId, accountId);
62
+
63
+ if (organisation === undefined) {
64
+ throw OrganisationError.notFound(organisationId);
65
+ }
66
+
67
+ if (member === undefined) {
68
+ throw MemberError.notFound(organisationId, accountId);
69
+ }
70
+
71
+ return { organisation, member };
72
+ }
73
+
74
+ private async endDelegationsGivenBy(organisationId: string, accountId: string, now: Date): Promise<void> {
75
+ const involving = await this.delegationRepository.findUnendedInvolving(organisationId, accountId);
76
+
77
+ for (const delegation of involving.filter((given) => given.delegatorId === accountId)) {
78
+ delegation.end(now);
79
+ await this.delegationRepository.save(delegation);
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,7 @@
1
+ import { AuthorizedCommand } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+
5
+ /** Deactivates the account of the person who asks. */
6
+ @Requires({{context.camel}}Permissions['account.deactivate'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
7
+ export class DeactivateAccountCommand extends AuthorizedCommand<string> {}
@@ -0,0 +1,69 @@
1
+ import type { Clock } from '@ddd-ts-kit/kernel';
2
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
3
+ import type { Member } from '{{alias.context}}/domain/aggregates/member.aggregate{{alias.ext}}';
4
+ import { AccountError } from '{{alias.context}}/domain/errors/account.error{{alias.ext}}';
5
+ import { OrganisationError } from '{{alias.context}}/domain/errors/organisation.error{{alias.ext}}';
6
+ import type { AccountRepository } from '{{alias.context}}/domain/repositories/account.repository{{alias.ext}}';
7
+ import type { DelegationRepository } from '{{alias.context}}/domain/repositories/delegation.repository{{alias.ext}}';
8
+ import type { MemberRepository } from '{{alias.context}}/domain/repositories/member.repository{{alias.ext}}';
9
+ import type { OrganisationRepository } from '{{alias.context}}/domain/repositories/organisation.repository{{alias.ext}}';
10
+ import { DeactivateAccountCommand } from '{{alias.context}}/application/commands/deactivate-account/deactivate-account.command{{alias.ext}}';
11
+
12
+ @CommandHandler(DeactivateAccountCommand)
13
+ export class DeactivateAccountHandler implements CommandHandler<DeactivateAccountCommand> {
14
+ constructor(
15
+ private readonly accountRepository: AccountRepository,
16
+ private readonly organisationRepository: OrganisationRepository,
17
+ private readonly memberRepository: MemberRepository,
18
+ private readonly delegationRepository: DelegationRepository,
19
+ private readonly clock: Clock,
20
+ ) {}
21
+
22
+ /**
23
+ * A person deactivates their own account, which leaves every organisation it belongs to and ends
24
+ * every delegation it gave or received there, in the same unit of work: a deactivated account
25
+ * holds no right anywhere, whatever its token says until it expires. The last owner of an
26
+ * organisation is refused: they appoint another owner first. The provider is asked to disable
27
+ * the person once it is committed (`application/integration/handlers/`).
28
+ */
29
+ async execute({ actor }: DeactivateAccountCommand): Promise<string> {
30
+ const account = await this.accountRepository.findById(actor.id);
31
+
32
+ if (account === undefined) {
33
+ throw AccountError.notFound(actor.id);
34
+ }
35
+
36
+ const now = this.clock.now();
37
+
38
+ account.deactivate(now);
39
+
40
+ for (const member of await this.memberRepository.searchCurrentOf(account.id)) {
41
+ await this.leave(member, now);
42
+ }
43
+
44
+ await this.accountRepository.save(account);
45
+
46
+ return account.id;
47
+ }
48
+
49
+ /** As a removal: the organisation saved unchanged orders the changes of its owners. */
50
+ private async leave(member: Member, now: Date): Promise<void> {
51
+ const { organisationId, accountId } = member;
52
+ const organisation = await this.organisationRepository.findById(organisationId);
53
+
54
+ if (organisation === undefined) {
55
+ throw OrganisationError.notFound(organisationId);
56
+ }
57
+
58
+ const owners = await this.memberRepository.countOwners(organisationId);
59
+
60
+ member.remove({ by: member.role, byAccount: accountId, delegationId: undefined, owners }, now);
61
+ await this.organisationRepository.save(organisation);
62
+ await this.memberRepository.save(member);
63
+
64
+ for (const delegation of await this.delegationRepository.findUnendedInvolving(organisationId, accountId)) {
65
+ delegation.end(now);
66
+ await this.delegationRepository.save(delegation);
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,14 @@
1
+ import { AuthorizedCommand, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+
5
+ /** Declines the invitation of this token, addressed to the actor. */
6
+ @Requires({{context.camel}}Permissions['invitation.decline'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
7
+ export class DeclineInvitationCommand extends AuthorizedCommand<string> {
8
+ constructor(
9
+ actor: Actor,
10
+ readonly token: string,
11
+ ) {
12
+ super(actor);
13
+ }
14
+ }
@@ -0,0 +1,36 @@
1
+ import type { Clock } from '@ddd-ts-kit/kernel';
2
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
3
+ import { AccountError } from '{{alias.context}}/domain/errors/account.error{{alias.ext}}';
4
+ import { InvitationError } from '{{alias.context}}/domain/errors/invitation.error{{alias.ext}}';
5
+ import type { AccountRepository } from '{{alias.context}}/domain/repositories/account.repository{{alias.ext}}';
6
+ import type { InvitationRepository } from '{{alias.context}}/domain/repositories/invitation.repository{{alias.ext}}';
7
+ import { DeclineInvitationCommand } from '{{alias.context}}/application/commands/decline-invitation/decline-invitation.command{{alias.ext}}';
8
+ import type { InvitationTokens } from '{{alias.context}}/application/ports/invitation-tokens.port{{alias.ext}}';
9
+
10
+ @CommandHandler(DeclineInvitationCommand)
11
+ export class DeclineInvitationHandler implements CommandHandler<DeclineInvitationCommand> {
12
+ constructor(
13
+ private readonly invitationRepository: InvitationRepository,
14
+ private readonly accountRepository: AccountRepository,
15
+ private readonly tokens: InvitationTokens,
16
+ private readonly clock: Clock,
17
+ ) {}
18
+
19
+ async execute({ actor, token }: DeclineInvitationCommand): Promise<string> {
20
+ const invitation = await this.invitationRepository.findByTokenHash(this.tokens.digestOf(token));
21
+ const account = await this.accountRepository.findById(actor.id);
22
+
23
+ if (invitation === undefined) {
24
+ throw InvitationError.notFound();
25
+ }
26
+
27
+ if (account === undefined) {
28
+ throw AccountError.notFound(actor.id);
29
+ }
30
+
31
+ invitation.decline(account, this.clock.now());
32
+ await this.invitationRepository.save(invitation);
33
+
34
+ return invitation.id;
35
+ }
36
+ }
@@ -0,0 +1,21 @@
1
+ import { AuthorizedCommand, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+ import type { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
5
+
6
+ /** Lends some of the rights of the actor in an organisation to another of its members, for a period. */
7
+ @Requires({{context.camel}}Permissions['delegation.grant'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
8
+ export class GrantDelegationCommand extends AuthorizedCommand<string> {
9
+ constructor(
10
+ actor: Actor,
11
+ readonly organisationId: string,
12
+ readonly terms: {
13
+ readonly delegateId: string;
14
+ readonly rights: readonly MemberRight[];
15
+ readonly startsAt: Date;
16
+ readonly endsAt: Date;
17
+ },
18
+ ) {
19
+ super(actor);
20
+ }
21
+ }
@@ -0,0 +1,43 @@
1
+ import type { Access } from '@ddd-ts-kit/auth';
2
+ import type { Clock, IdGenerator } from '@ddd-ts-kit/kernel';
3
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
4
+ import { Delegation } from '{{alias.context}}/domain/aggregates/delegation.aggregate{{alias.ext}}';
5
+ import { MemberError } from '{{alias.context}}/domain/errors/member.error{{alias.ext}}';
6
+ import type { DelegationRepository } from '{{alias.context}}/domain/repositories/delegation.repository{{alias.ext}}';
7
+ import type { MemberRepository } from '{{alias.context}}/domain/repositories/member.repository{{alias.ext}}';
8
+ import { RoleRightsService } from '{{alias.context}}/domain/services/role-rights.service{{alias.ext}}';
9
+ import { DelegationPeriod } from '{{alias.context}}/domain/value-objects/delegation-period.vo{{alias.ext}}';
10
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
11
+ import { GrantDelegationCommand } from '{{alias.context}}/application/commands/grant-delegation/grant-delegation.command{{alias.ext}}';
12
+
13
+ @CommandHandler(GrantDelegationCommand)
14
+ export class GrantDelegationHandler implements CommandHandler<GrantDelegationCommand> {
15
+ constructor(
16
+ private readonly delegationRepository: DelegationRepository,
17
+ private readonly memberRepository: MemberRepository,
18
+ private readonly access: Access,
19
+ private readonly ids: IdGenerator,
20
+ private readonly clock: Clock,
21
+ ) {}
22
+
23
+ /** A member lends what their role grants, never what was lent to them, to another member of the organisation. */
24
+ async execute({ actor, organisationId, terms }: GrantDelegationCommand): Promise<string> {
25
+ const now = this.clock.now();
26
+ const delegator = await this.memberRepository.findOf(organisationId, actor.id);
27
+ const held = new RoleRightsService().rightsIn(organisationId, actor.id, { member: delegator, lent: [], now });
28
+
29
+ await this.access.authorize(actor, {{context.camel}}Permissions['delegation.grant'], held);
30
+
31
+ if ((await this.memberRepository.findOf(organisationId, terms.delegateId)) === undefined) {
32
+ throw MemberError.notFound(organisationId, terms.delegateId);
33
+ }
34
+
35
+ const period = DelegationPeriod.create(terms.startsAt, terms.endsAt);
36
+ const lent = { organisationId, delegatorId: actor.id, delegateId: terms.delegateId, rights: terms.rights, period };
37
+ const delegation = Delegation.grant(this.ids.next(), { ...lent, held }, now);
38
+
39
+ await this.delegationRepository.save(delegation);
40
+
41
+ return delegation.id;
42
+ }
43
+ }
@@ -0,0 +1,16 @@
1
+ import { AuthorizedCommand, type Actor } from '@ddd-ts-kit/kernel';
2
+ import { Requires } from '@ddd-ts-kit/kernel/declarations';
3
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
4
+ import type { MemberRole } from '{{alias.context}}/domain/vocabularies/member-role.vocabulary{{alias.ext}}';
5
+
6
+ /** Invites a person to an organisation by email address, in a role: the answer carries the token to send them, never stored. */
7
+ @Requires({{context.camel}}Permissions['member.invite'], { resource: () => ({ type: 'context', id: '{{context.kebab}}' }) })
8
+ export class InviteMemberCommand extends AuthorizedCommand<{ readonly id: string; readonly token: string }> {
9
+ constructor(
10
+ actor: Actor,
11
+ readonly organisationId: string,
12
+ readonly invitee: { readonly email: string; readonly role: MemberRole },
13
+ ) {
14
+ super(actor);
15
+ }
16
+ }
@@ -0,0 +1,53 @@
1
+ import type { Access } from '@ddd-ts-kit/auth';
2
+ import type { Clock, IdGenerator } from '@ddd-ts-kit/kernel';
3
+ import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
4
+ import { Invitation } from '{{alias.context}}/domain/aggregates/invitation.aggregate{{alias.ext}}';
5
+ import { Member } from '{{alias.context}}/domain/aggregates/member.aggregate{{alias.ext}}';
6
+ import type { DelegationRepository } from '{{alias.context}}/domain/repositories/delegation.repository{{alias.ext}}';
7
+ import type { InvitationRepository } from '{{alias.context}}/domain/repositories/invitation.repository{{alias.ext}}';
8
+ import type { MemberRepository } from '{{alias.context}}/domain/repositories/member.repository{{alias.ext}}';
9
+ import { RoleRightsService } from '{{alias.context}}/domain/services/role-rights.service{{alias.ext}}';
10
+ import { EmailAddress } from '{{alias.context}}/domain/value-objects/email-address.vo{{alias.ext}}';
11
+ import { MemberRight } from '{{alias.context}}/domain/vocabularies/member-right.vocabulary{{alias.ext}}';
12
+ import { MemberRole } from '{{alias.context}}/domain/vocabularies/member-role.vocabulary{{alias.ext}}';
13
+ import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
14
+ import { InviteMemberCommand } from '{{alias.context}}/application/commands/invite-member/invite-member.command{{alias.ext}}';
15
+ import type { InvitationTokens } from '{{alias.context}}/application/ports/invitation-tokens.port{{alias.ext}}';
16
+
17
+ @CommandHandler(InviteMemberCommand)
18
+ export class InviteMemberHandler implements CommandHandler<InviteMemberCommand> {
19
+ constructor(
20
+ private readonly invitationRepository: InvitationRepository,
21
+ private readonly memberRepository: MemberRepository,
22
+ private readonly delegationRepository: DelegationRepository,
23
+ private readonly tokens: InvitationTokens,
24
+ private readonly access: Access,
25
+ private readonly ids: IdGenerator,
26
+ private readonly clock: Clock,
27
+ ) {}
28
+
29
+ /**
30
+ * Whoever may invite in the organisation invites, in their own role or below (never above it,
31
+ * whatever a delegation lends them); the delegation used, if any, is named in the fact. The
32
+ * token leaves in the answer, its digest stays.
33
+ */
34
+ async execute({ actor, organisationId, invitee }: InviteMemberCommand): Promise<{ id: string; token: string }> {
35
+ const now = this.clock.now();
36
+ const inviter = await this.memberRepository.findOf(organisationId, actor.id);
37
+ const held = { member: inviter, lent: await this.delegationRepository.findUnendedTo(organisationId, actor.id), now };
38
+ const rights = new RoleRightsService();
39
+
40
+ await this.access.authorize(actor, {{context.camel}}Permissions['member.invite'], rights.rightsIn(organisationId, actor.id, held));
41
+
42
+ Member.assertAppointable(organisationId, invitee.role, inviter?.role ?? MemberRole.Member);
43
+
44
+ const { token, digest } = this.tokens.issue();
45
+ const delegationId = rights.delegationUsedFor(MemberRight.InviteMembers, held);
46
+ const terms = { organisationId, email: EmailAddress.create(invitee.email), role: invitee.role, tokenHash: digest, invitedBy: actor.id };
47
+ const invitation = Invitation.issue(this.ids.next(), { ...terms, ...(delegationId === undefined ? {} : { delegationId }) }, now);
48
+
49
+ await this.invitationRepository.save(invitation);
50
+
51
+ return { id: invitation.id, token };
52
+ }
53
+ }