@things-factory/auth-base 10.1.278 → 10.1.280

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 (284) hide show
  1. package/package.json +24 -7
  2. package/CHANGELOG.md +0 -55
  3. package/dist-client/actions/auth.js.map +0 -1
  4. package/dist-client/apply-domain-theme.js.map +0 -1
  5. package/dist-client/auth.js.map +0 -1
  6. package/dist-client/bootstrap.js.map +0 -1
  7. package/dist-client/directive/privileged.js.map +0 -1
  8. package/dist-client/domain-required.js.map +0 -1
  9. package/dist-client/index.js.map +0 -1
  10. package/dist-client/profiled.js.map +0 -1
  11. package/dist-client/reducers/auth.js.map +0 -1
  12. package/dist-client/tsconfig.tsbuildinfo +0 -1
  13. package/dist-client/verify-webauthn.js.map +0 -1
  14. package/dist-server/ai-entry/access-assistant.js.map +0 -1
  15. package/dist-server/ai-entry/access-entry-targets.js.map +0 -1
  16. package/dist-server/ai-entry/entry-target-shape.js.map +0 -1
  17. package/dist-server/constants/error-code.js.map +0 -1
  18. package/dist-server/constants/error-message.js.map +0 -1
  19. package/dist-server/constants/max-age.js.map +0 -1
  20. package/dist-server/controllers/access-request.js.map +0 -1
  21. package/dist-server/controllers/account-mail.js.map +0 -1
  22. package/dist-server/controllers/auth.js.map +0 -1
  23. package/dist-server/controllers/change-pwd.js.map +0 -1
  24. package/dist-server/controllers/confirm-identity.js.map +0 -1
  25. package/dist-server/controllers/delete-user.js.map +0 -1
  26. package/dist-server/controllers/passkey.js.map +0 -1
  27. package/dist-server/controllers/password-setup.js.map +0 -1
  28. package/dist-server/controllers/profile.js.map +0 -1
  29. package/dist-server/controllers/reset-password.js.map +0 -1
  30. package/dist-server/controllers/signin.js.map +0 -1
  31. package/dist-server/controllers/signup.js.map +0 -1
  32. package/dist-server/controllers/unlock-user.js.map +0 -1
  33. package/dist-server/controllers/utils/find-verification-token.js.map +0 -1
  34. package/dist-server/controllers/utils/make-verification-token.js.map +0 -1
  35. package/dist-server/controllers/utils/password-rule.js.map +0 -1
  36. package/dist-server/controllers/utils/save-verification-token.js.map +0 -1
  37. package/dist-server/controllers/verification.js.map +0 -1
  38. package/dist-server/errors/auth-error.js.map +0 -1
  39. package/dist-server/errors/index.js.map +0 -1
  40. package/dist-server/errors/user-domain-not-match-error.js.map +0 -1
  41. package/dist-server/index.js.map +0 -1
  42. package/dist-server/middlewares/audit-scope-middleware.js.map +0 -1
  43. package/dist-server/middlewares/authenticate-401-middleware.js.map +0 -1
  44. package/dist-server/middlewares/bypass-signin-middleware.js.map +0 -1
  45. package/dist-server/middlewares/domain-authenticate-middleware.js.map +0 -1
  46. package/dist-server/middlewares/graphql-authenticate-middleware.js.map +0 -1
  47. package/dist-server/middlewares/index.js.map +0 -1
  48. package/dist-server/middlewares/jwt-authenticate-middleware.js.map +0 -1
  49. package/dist-server/middlewares/signin-middleware.js.map +0 -1
  50. package/dist-server/middlewares/verify-recaptcha-middleware.js.map +0 -1
  51. package/dist-server/migrations/1548206416130-SeedUser.js.map +0 -1
  52. package/dist-server/migrations/1566805283882-SeedPrivilege.js.map +0 -1
  53. package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js.map +0 -1
  54. package/dist-server/migrations/index.js.map +0 -1
  55. package/dist-server/role-tasks.js.map +0 -1
  56. package/dist-server/router/auth-checkin-router.js.map +0 -1
  57. package/dist-server/router/auth-private-process-router.js.map +0 -1
  58. package/dist-server/router/auth-public-process-router.js.map +0 -1
  59. package/dist-server/router/auth-signin-router.js.map +0 -1
  60. package/dist-server/router/auth-signup-router.js.map +0 -1
  61. package/dist-server/router/index.js.map +0 -1
  62. package/dist-server/router/oauth2/index.js.map +0 -1
  63. package/dist-server/router/oauth2/oauth2-authorize-router.js.map +0 -1
  64. package/dist-server/router/oauth2/oauth2-router.js.map +0 -1
  65. package/dist-server/router/oauth2/oauth2-server.js.map +0 -1
  66. package/dist-server/router/oauth2/passport-oauth2-client-password.js.map +0 -1
  67. package/dist-server/router/oauth2/passport-refresh-token.js.map +0 -1
  68. package/dist-server/router/path-base-domain-router.js.map +0 -1
  69. package/dist-server/router/site-root-router.js.map +0 -1
  70. package/dist-server/router/webauthn-router.js.map +0 -1
  71. package/dist-server/routes.js.map +0 -1
  72. package/dist-server/service/access-request/access-request-mutation.js.map +0 -1
  73. package/dist-server/service/access-request/access-request-query.js.map +0 -1
  74. package/dist-server/service/access-request/access-request-types.js.map +0 -1
  75. package/dist-server/service/access-request/access-request.js.map +0 -1
  76. package/dist-server/service/access-request/index.js.map +0 -1
  77. package/dist-server/service/app-binding/app-binding-mutation.js.map +0 -1
  78. package/dist-server/service/app-binding/app-binding-query.js.map +0 -1
  79. package/dist-server/service/app-binding/app-binding-types.js.map +0 -1
  80. package/dist-server/service/app-binding/app-binding.js.map +0 -1
  81. package/dist-server/service/app-binding/index.js.map +0 -1
  82. package/dist-server/service/appliance/appliance-mutation.js.map +0 -1
  83. package/dist-server/service/appliance/appliance-query.js.map +0 -1
  84. package/dist-server/service/appliance/appliance-types.js.map +0 -1
  85. package/dist-server/service/appliance/appliance.js.map +0 -1
  86. package/dist-server/service/appliance/index.js.map +0 -1
  87. package/dist-server/service/application/application-mutation.js.map +0 -1
  88. package/dist-server/service/application/application-query.js.map +0 -1
  89. package/dist-server/service/application/application-types.js.map +0 -1
  90. package/dist-server/service/application/application.js.map +0 -1
  91. package/dist-server/service/application/index.js.map +0 -1
  92. package/dist-server/service/audit/audit-action.js.map +0 -1
  93. package/dist-server/service/audit/audit-kinds.js.map +0 -1
  94. package/dist-server/service/audit/audit-query.js.map +0 -1
  95. package/dist-server/service/audit/audit-recorder.js.map +0 -1
  96. package/dist-server/service/audit/audit-request.js.map +0 -1
  97. package/dist-server/service/audit/audit-retention.js.map +0 -1
  98. package/dist-server/service/audit/audit-subscriber.js.map +0 -1
  99. package/dist-server/service/audit/audit-target.js.map +0 -1
  100. package/dist-server/service/audit/audit-types.js.map +0 -1
  101. package/dist-server/service/audit/audit-work.js.map +0 -1
  102. package/dist-server/service/audit/index.js.map +0 -1
  103. package/dist-server/service/auth-provider/auth-provider-mutation.js.map +0 -1
  104. package/dist-server/service/auth-provider/auth-provider-parameter-spec.js.map +0 -1
  105. package/dist-server/service/auth-provider/auth-provider-query.js.map +0 -1
  106. package/dist-server/service/auth-provider/auth-provider-type.js.map +0 -1
  107. package/dist-server/service/auth-provider/auth-provider.js.map +0 -1
  108. package/dist-server/service/auth-provider/index.js.map +0 -1
  109. package/dist-server/service/domain-generator/domain-generator-mutation.js.map +0 -1
  110. package/dist-server/service/domain-generator/domain-generator-types.js.map +0 -1
  111. package/dist-server/service/domain-generator/index.js.map +0 -1
  112. package/dist-server/service/domain-link/domain-link-mutation.js.map +0 -1
  113. package/dist-server/service/domain-link/domain-link-query.js.map +0 -1
  114. package/dist-server/service/domain-link/domain-link-types.js.map +0 -1
  115. package/dist-server/service/domain-link/domain-link.js.map +0 -1
  116. package/dist-server/service/domain-link/index.js.map +0 -1
  117. package/dist-server/service/domain-owner/domain-owner-mutation.js.map +0 -1
  118. package/dist-server/service/domain-owner/domain-owner-query.js.map +0 -1
  119. package/dist-server/service/domain-owner/domain-owner.js.map +0 -1
  120. package/dist-server/service/domain-owner/index.js.map +0 -1
  121. package/dist-server/service/granted-role/granted-role-mutation.js.map +0 -1
  122. package/dist-server/service/granted-role/granted-role-query.js.map +0 -1
  123. package/dist-server/service/granted-role/granted-role.js.map +0 -1
  124. package/dist-server/service/granted-role/index.js.map +0 -1
  125. package/dist-server/service/identity-confirmation/identity-confirmation.js.map +0 -1
  126. package/dist-server/service/index.js.map +0 -1
  127. package/dist-server/service/open-fields.js.map +0 -1
  128. package/dist-server/service/partner/index.js.map +0 -1
  129. package/dist-server/service/partner/partner-mutation.js.map +0 -1
  130. package/dist-server/service/partner/partner-query.js.map +0 -1
  131. package/dist-server/service/partner/partner-types.js.map +0 -1
  132. package/dist-server/service/partner/partner.js.map +0 -1
  133. package/dist-server/service/password-history/index.js.map +0 -1
  134. package/dist-server/service/password-history/password-history.js.map +0 -1
  135. package/dist-server/service/privilege/index.js.map +0 -1
  136. package/dist-server/service/privilege/open-field-rule.js.map +0 -1
  137. package/dist-server/service/privilege/privilege-axis.js.map +0 -1
  138. package/dist-server/service/privilege/privilege-deprecation.js.map +0 -1
  139. package/dist-server/service/privilege/privilege-directive.js.map +0 -1
  140. package/dist-server/service/privilege/privilege-mutation.js.map +0 -1
  141. package/dist-server/service/privilege/privilege-query.js.map +0 -1
  142. package/dist-server/service/privilege/privilege-rejection.js.map +0 -1
  143. package/dist-server/service/privilege/privilege-sync.js.map +0 -1
  144. package/dist-server/service/privilege/privilege-types.js.map +0 -1
  145. package/dist-server/service/privilege/privilege.js.map +0 -1
  146. package/dist-server/service/role/index.js.map +0 -1
  147. package/dist-server/service/role/role-mutation.js.map +0 -1
  148. package/dist-server/service/role/role-privilege-query.js.map +0 -1
  149. package/dist-server/service/role/role-query.js.map +0 -1
  150. package/dist-server/service/role/role-types.js.map +0 -1
  151. package/dist-server/service/role/role.js.map +0 -1
  152. package/dist-server/service/role-template/index.js.map +0 -1
  153. package/dist-server/service/role-template/role-task.js.map +0 -1
  154. package/dist-server/service/role-template/role-template-gap-resolver.js.map +0 -1
  155. package/dist-server/service/role-template/role-template-mutation.js.map +0 -1
  156. package/dist-server/service/role-template/role-template-query.js.map +0 -1
  157. package/dist-server/service/role-template/role-template-types.js.map +0 -1
  158. package/dist-server/service/role-template/role-template.js.map +0 -1
  159. package/dist-server/service/user/domain-query.js.map +0 -1
  160. package/dist-server/service/user/index.js.map +0 -1
  161. package/dist-server/service/user/user-mutation.js.map +0 -1
  162. package/dist-server/service/user/user-query.js.map +0 -1
  163. package/dist-server/service/user/user-types.js.map +0 -1
  164. package/dist-server/service/user/user.js.map +0 -1
  165. package/dist-server/service/users-auth-providers/index.js.map +0 -1
  166. package/dist-server/service/users-auth-providers/users-auth-providers.js.map +0 -1
  167. package/dist-server/service/verification-token/index.js.map +0 -1
  168. package/dist-server/service/verification-token/verification-token.js.map +0 -1
  169. package/dist-server/service/web-auth-credential/index.js.map +0 -1
  170. package/dist-server/service/web-auth-credential/web-auth-credential-mutation.js.map +0 -1
  171. package/dist-server/service/web-auth-credential/web-auth-credential-query.js.map +0 -1
  172. package/dist-server/service/web-auth-credential/web-auth-credential.js.map +0 -1
  173. package/dist-server/tsconfig.tsbuildinfo +0 -1
  174. package/dist-server/types.js.map +0 -1
  175. package/dist-server/utils/accepts.js.map +0 -1
  176. package/dist-server/utils/access-request-review.js.map +0 -1
  177. package/dist-server/utils/access-token-cookie.js.map +0 -1
  178. package/dist-server/utils/account-lock-rule.js.map +0 -1
  179. package/dist-server/utils/assignable-roles.js.map +0 -1
  180. package/dist-server/utils/build-auth-url.js.map +0 -1
  181. package/dist-server/utils/can-user-checkin-domain.js.map +0 -1
  182. package/dist-server/utils/check-permission.js.map +0 -1
  183. package/dist-server/utils/check-user-belongs-domain.js.map +0 -1
  184. package/dist-server/utils/check-user-has-role.js.map +0 -1
  185. package/dist-server/utils/checkin-domain-facts.js.map +0 -1
  186. package/dist-server/utils/checkin-domain-rule.js.map +0 -1
  187. package/dist-server/utils/credential-at-rest.js.map +0 -1
  188. package/dist-server/utils/credential-features.js.map +0 -1
  189. package/dist-server/utils/credential-serial-rule.js.map +0 -1
  190. package/dist-server/utils/domain-ownership.js.map +0 -1
  191. package/dist-server/utils/encrypt-state.js.map +0 -1
  192. package/dist-server/utils/get-aes-256-key.js.map +0 -1
  193. package/dist-server/utils/get-domain-users.js.map +0 -1
  194. package/dist-server/utils/get-secret.js.map +0 -1
  195. package/dist-server/utils/get-user-domains.js.map +0 -1
  196. package/dist-server/utils/lock-duration.js.map +0 -1
  197. package/dist-server/utils/password-reset-rule.js.map +0 -1
  198. package/dist-server/utils/profile-user.js.map +0 -1
  199. package/dist-server/utils/refuse-role-name.js.map +0 -1
  200. package/dist-server/utils/role-name-standing.js.map +0 -1
  201. package/dist-server/utils/same-origin-redirect.js.map +0 -1
  202. package/dist-server/utils/verification-token-rule.js.map +0 -1
  203. package/spec/unit/checkin-domain.spec.ts +0 -307
  204. package/spec/unit/checkin-privilege.spec.ts +0 -205
  205. package/spec/unit/domain-owner-entity.spec.ts +0 -179
  206. package/spec/unit/domain-owner-granted.spec.ts +0 -215
  207. package/spec/unit/domain-owner-migration.spec.ts +0 -236
  208. package/spec/unit/domain-owner-mutation.spec.ts +0 -337
  209. package/spec/unit/domain-ownership.spec.ts +0 -188
  210. package/tests/access-assistant-db.test.ts +0 -155
  211. package/tests/access-entry-targets-db.test.ts +0 -176
  212. package/tests/access-request-db.test.ts +0 -394
  213. package/tests/access-request-pg.test.ts +0 -83
  214. package/tests/access-request-review.test.ts +0 -193
  215. package/tests/account-lock-db.test.ts +0 -216
  216. package/tests/account-lock-rule.test.ts +0 -141
  217. package/tests/account-mail.test.ts +0 -69
  218. package/tests/activate-page-case.test.ts +0 -53
  219. package/tests/add-user-gates.test.ts +0 -34
  220. package/tests/app-binding-db.test.ts +0 -210
  221. package/tests/app-binding-delete-db.test.ts +0 -188
  222. package/tests/appliance-credential-state-db.test.ts +0 -207
  223. package/tests/appliance-delete-db.test.ts +0 -119
  224. package/tests/appliance-revoke-db.test.ts +0 -294
  225. package/tests/appliance-schema.test.ts +0 -129
  226. package/tests/application-delete-db.test.ts +0 -190
  227. package/tests/application-token-payload-db.test.ts +0 -185
  228. package/tests/assignable-roles-db.test.ts +0 -219
  229. package/tests/audit-db.test.ts +0 -410
  230. package/tests/audit-work.test.ts +0 -58
  231. package/tests/auth-base-role-tasks.test.ts +0 -58
  232. package/tests/checkin-domain-rule.test.ts +0 -128
  233. package/tests/checkin-privilege-seam-db.test.ts +0 -158
  234. package/tests/compiled.ts +0 -41
  235. package/tests/confirm-identity-db.test.ts +0 -85
  236. package/tests/credential-at-rest-db.test.ts +0 -246
  237. package/tests/credential-features-off-db.test.ts +0 -189
  238. package/tests/credential-serial-rule.test.ts +0 -84
  239. package/tests/declare-privileges.test.ts +0 -29
  240. package/tests/domain-assignees-db.test.ts +0 -95
  241. package/tests/domain-inheritance-sentinel.test.ts +0 -93
  242. package/tests/domain-required.test.ts +0 -127
  243. package/tests/has-privilege-db.test.ts +0 -198
  244. package/tests/identity-confirmation.test.ts +0 -146
  245. package/tests/list-inheritance-db.test.ts +0 -127
  246. package/tests/lock-recovery.test.ts +0 -206
  247. package/tests/mail-request-answers-db.test.ts +0 -142
  248. package/tests/open-doors.test.ts +0 -121
  249. package/tests/open-fields-enforcement.test.ts +0 -215
  250. package/tests/open-fields-framework.test.ts +0 -212
  251. package/tests/open-redirect-db.test.ts +0 -143
  252. package/tests/passkey-authenticator.ts +0 -104
  253. package/tests/passkey-db.test.ts +0 -295
  254. package/tests/passkey-signin-route-db.test.ts +0 -139
  255. package/tests/password-hashing.test.ts +0 -131
  256. package/tests/password-persistence.test.ts +0 -147
  257. package/tests/password-reset-rule-db.test.ts +0 -159
  258. package/tests/password-setup-db.test.ts +0 -215
  259. package/tests/permission-gate.test.ts +0 -228
  260. package/tests/picture-reducer.test.ts +0 -25
  261. package/tests/privilege-axis.test.ts +0 -157
  262. package/tests/privilege-deprecation.test.ts +0 -65
  263. package/tests/privilege-directive.test.ts +0 -332
  264. package/tests/privilege-rejection.test.ts +0 -104
  265. package/tests/privilege-sync-db.test.ts +0 -58
  266. package/tests/profile-user.test.ts +0 -24
  267. package/tests/refusal-cause.test.ts +0 -80
  268. package/tests/refusals-have-korean.test.ts +0 -24
  269. package/tests/request-holds.test.ts +0 -53
  270. package/tests/role-inheritance-db.test.ts +0 -286
  271. package/tests/role-mutation-db.test.ts +0 -293
  272. package/tests/role-name-lookup-sentinel.test.ts +0 -79
  273. package/tests/role-name-standing.test.ts +0 -83
  274. package/tests/role-privileges-db.test.ts +0 -252
  275. package/tests/role-task.test.ts +0 -168
  276. package/tests/role-template-gap-db.test.ts +0 -268
  277. package/tests/role-template-seed-db.test.ts +0 -180
  278. package/tests/role-template.test.ts +0 -162
  279. package/tests/role-users-gate.test.ts +0 -22
  280. package/tests/signin-policy.test.ts +0 -200
  281. package/tests/signup-duplicate-db.test.ts +0 -68
  282. package/tests/token-issuance.test.ts +0 -244
  283. package/tests/user-roles-field-db.test.ts +0 -134
  284. package/tests/verification-token-db.test.ts +0 -285
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-mutation.js","sourceRoot":"","sources":["../../../server/service/role/role-mutation.ts"],"names":[],"mappings":";;;;AAAA,qCAA4B;AAC5B,+CAAqF;AAErF,iDAA6E;AAE7E,yEAA0E;AAE1E,4DAAqD;AACrD,wEAAoE;AACpE,6CAAsC;AACtC,uCAAgC;AAChC,kEAA8D;AAC9D,mDAAoD;AAEpD;;;;;;GAMG;AAGI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACvB;;;;;OAKG;IAGG,AAAN,KAAK,CAAC,UAAU,CAAc,IAAa,EAAS,OAAwB;QAC1E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAEjC,MAAM,IAAA,8CAAwB,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;QAEvF,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YAC9C,IAAI,CAAC,UAAU,GAAG,MAAM,IAAA,qBAAa,EAAC,wBAAS,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5G,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;QACnF,CAAC;QAED,OAAO,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,IAAI,CAAC;YACpC,MAAM;YACN,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,GAAG,IAAI;SACR,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,UAAU,CACH,EAAU,EACP,KAAgB,EACvB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,KAAK,CAAA;QAC/C,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAA;QAC1C,MAAM,IAAI,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC9C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;YACxC,SAAS,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,SAAS,CAAC;SAChD,CAAC,CAAA;QAEF;;;;WAIG;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjE,yEAAyE,CAC5E,CAAA;QACH,CAAC;QAED,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;YAE5B,MAAM,IAAA,8CAAwB,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAA;YAExF,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAA;QACtB,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAChC,CAAC;QAED,kGAAkG;QAClG,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,IAAA,mCAAgB,GAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/F,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,UAAU,yBAAyB,CAAC,CAAA;QAC1F,CAAC;QAED,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC;YAC/B,GAAG,IAAI;YACP,GAAG,KAAK;YACR;;;eAGG;YACH,UAAU,EAAE,UAAU;gBACpB,CAAC,CAAC,MAAM,IAAA,qBAAa,EAAC,wBAAS,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBAC9F,CAAC,CAAC,IAAI,CAAC,UAAU;YACnB,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;IACX,CAAC;IAED;;;;;;;;;;;;;;OAcG;IAIG,AAAN,KAAK,CAAC,kBAAkB,CACe,OAAoB,EAClD,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,cAAc,GAAG,EAAE,CAAC,aAAa,CAAC,cAAI,CAAC,CAAA;QAC7C,MAAM,OAAO,GAAW,EAAE,CAAA;QAE1B,8DAA8D;QAC9D,MAAM,MAAM,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;QAEvE,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;YAC/B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;YACxF,CAAC;YAED,MAAM,IAAA,8CAAwB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC,CAAA;YAElF,OAAO,CAAC,IAAI,CACV,MAAM,cAAc,CAAC,IAAI,CAAC;gBACxB,MAAM;gBACN,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,IAAI;gBACb,IAAI;gBACJ,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CACH,CAAA;QACH,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;YAEjG,qDAAqD;YACrD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;oBACjE,yEAAyE,CAC5E,CAAA;YACH,CAAC;YAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;YAC/B,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC/B,MAAM,IAAA,8CAAwB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC,CAAA;gBAElF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;YAClB,CAAC;YAED,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBACpC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAA;YACtC,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACrE,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;IAKK,AAAN,KAAK,CAAC,UAAU,CAAY,EAAU,EAAS,OAAwB;QACrE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,IAAI,IAAI,GAAS,MAAM,EAAE;aACtB,aAAa,CAAC,cAAI,CAAC;aACnB,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC,CAAA;QAE5F;;;;;;WAMG;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjE,0EAA0E,CAC7E,CAAA;QACH,CAAC;QAED,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QAExB,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,EAAE,CAAA;YAEpB,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,cAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAE9C,MAAM,EAAE,CAAC,aAAa,CAAC,cAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAE5C,6GAA6G;YAC7G,MAAM,IAAA,qCAAiB,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;YAElI,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,8CAA8C;YAC9C,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CAA+B,GAAa,EAAS,OAAwB;QAC5F,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC;;;;;;;;;WASG;QACH,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,IAAI,CAAC;YAC3C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE;YACjD,SAAS,EAAE,CAAC,OAAO,CAAC;SACrB,CAAC,CAAA;QAEF,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAEvB,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC5C,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;YACrB,yEAAyE;YACzE,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE;gBAC5D,MAAM,OAAO,CAAC,aAAa,CAAC,cAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;gBACnD,MAAM,IAAA,qCAAiB,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;YAChI,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA;AAhPY,oCAAY;AASjB;IAFL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;IAC/C,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IAAiB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAf,uBAAO;;8CAoB1C;AAIK;IAFL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IAEtE,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IACT,mBAAA,IAAA,kBAAG,EAAC,OAAO,CAAC,CAAA;IACZ,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDADe,yBAAS;;8CAoD/B;AAoBK;IAHL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,cAAI,CAAC,EAAE,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;IAExF,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,yBAAS,CAAC,CAAC,CAAA;IACnC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAsDP;AAKK;IAHL,IAAA,wBAAS,EAAC,qDAAqD,CAAC;IAChE,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAC9C,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8CAqC7C;AAIK;IAFL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,0BAA0B,EAAE,CAAC;IACvD,mBAAA,IAAA,kBAAG,EAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAAiB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+CA8BpE;uBA/OU,YAAY;IAFxB,IAAA,uBAAQ,EAAC,cAAI,CAAC;IACd,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,YAAY,CAgPxB;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACrD,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAM;IAExB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChF,MAAM,IAAI,eAAO,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,EAAE,2DAA2D,KAAK,kCAAkC,CAAC,CAAA;AACjJ,CAAC","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { getRepository, Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { refuseRoleNameIfStanding } from '../../utils/refuse-role-name.js'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { allRoleTemplates } from '../role-template/role-template.js'\nimport { User } from '../user/user.js'\nimport { Role } from './role.js'\nimport { recordAuditAction } from '../audit/audit-recorder.js'\nimport { NewRole, RolePatch } from './role-types.js'\n\n/*\n * ── Why this class passes through `SpeakRefusals` ───────────────────────────\n * `refuseRoleNameIfStanding` throws a `Refusal` — a name and values rather than a sentence\n * (ADR-0054), so the wording is made where the request's language is known. This middleware is\n * the exit that turns it into one. The older `new Error('...')` sentences in here are untouched\n * and pass through it unchanged; moving them is a separate job.\n */\n@Resolver(Role)\n@UseMiddleware(SpeakRefusals)\nexport class RoleMutation {\n /*\n * 선언이 `User` 였다 — 돌려주는 것은 `Role` 인데(아래 TS 반환형도 그렇다) 스키마에는 사용자로\n * 적혀 있었다. 두 이름에 겹치는 칸(`id`·`name`·`description`)만 물어서 지금까지 드러나지\n * 않았고, `email` 을 물으면 오류 없이 빈 값이 온다. 부르는 자리는 하나뿐이고 겹치는 칸만\n * 쓴다(`role-list-page.ts` 의 `ox-record-creator`).\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Role, { description: 'To create new role' })\n async createRole(@Arg('role') role: NewRole, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain, user } = context.state\n const roleName = role.name.trim()\n\n await refuseRoleNameIfStanding({ name: roleName, domain, separately: role.separately })\n\n if (role.privileges && role.privileges.length) {\n role.privileges = await getRepository(Privilege).findByIds(role.privileges.map(privilege => privilege.id))\n }\n\n if (role.users && role.users.length) {\n role.users = await getRepository(User).findByIds(role.users.map(user => user.id))\n }\n\n return await getRepository(Role).save({\n domain,\n updater: user,\n creator: user,\n ...role\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Role, { description: 'To modify role information' })\n async updateRole(\n @Arg('id') id: string,\n @Arg('patch') patch: RolePatch,\n @Ctx() context: ResolverContext\n ): Promise<Role> {\n const { domain, user } = context.state\n const { name, description, privileges } = patch\n const roleRepository = getRepository(Role)\n const role: Role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['privileges', 'creator', 'updater']\n })\n\n /*\n * **못 찾았으면 그렇게 말한다.** 이 범위는 `domain.id` 하나다 — 목록은 상속으로 부모 도메인의\n * 역할까지 보여 주므로(`inherited: Include`) 관리자가 그것을 고치려 하면 여기로 온다. 예전에는\n * 바로 아래 `role.name` 에서 TypeError 가 나 500 이 되었고, 화면은 왜 안 되는지 알 수 없었다.\n */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be edited in that domain.'\n )\n }\n\n if (name && role.name !== name) {\n const roleName = name.trim()\n\n await refuseRoleNameIfStanding({ name: roleName, domain, separately: patch.separately })\n\n role.name = roleName\n }\n\n if (description) {\n role.description = description\n }\n\n /* A base template is one this installation declares (ADR-0075 decision 1, amended 2026-10-26). */\n if (patch.templateId && !allRoleTemplates().some(template => template.id === patch.templateId)) {\n throw new Error(`There is no role template \"${patch.templateId}\" in this installation.`)\n }\n\n return await roleRepository.save({\n ...role,\n ...patch,\n /*\n * `privileges` 를 안 보낸 patch 는 **권한을 건드리지 않는다는 뜻**이다. 예전에는 여기서\n * `patch.privileges.map` 이 TypeError 를 냈다 — 이름만 고치려는 호출이 500 이었다.\n */\n privileges: privileges\n ? await getRepository(Privilege).findBy({ id: In(privileges.map(privilege => privilege.id)) })\n : role.privileges,\n updater: user\n } as any)\n }\n\n /**\n * 격자에서 고친 여러 줄을 한 번에 저장한다.\n *\n * 이 저장소의 목록 화면 관례다 — `ox-grist` 의 `dirtyRecords` 를 `cuFlag` 와 함께 보내면\n * `+` 는 만들고 `M` 은 고친다(`updateMultipleAuthProvider`·`updateMultipleUser`·\n * `updateMultipleDomainLink` 가 같은 모양). 역할에만 이 자리가 없어서 이름·설명을 격자에서\n * 고칠 수 없었다.\n *\n * **`privileges` 는 여기서 안 건드린다.** 격자는 이름과 설명만 고치고, 권한은 전용 화면이\n * 쓴다(`updateRole`). 안 보낸 것을 「비우라」로 읽으면 격자에서 이름만 고친 순간 그 역할이\n * 아무것도 안 여는 역할이 된다.\n *\n * 지우기는 이 자리에 없다 — 격자의 삭제는 `deleteRoles` 가 받고, 그쪽은 **쓰이는 역할을\n * 남긴다**는 규칙을 따로 갖는다.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [Role], { description: 'To create or update multiple roles at once' })\n async updateMultipleRole(\n @Arg('patches', type => [RolePatch]) patches: RolePatch[],\n @Ctx() context: ResolverContext\n ): Promise<Role[]> {\n const { domain, user, tx } = context.state\n const roleRepository = tx.getRepository(Role)\n const results: Role[] = []\n\n /* `cuFlag` 가 없는 줄은 무엇을 하라는 것인지 알 수 없다 — 짐작해서 만들거나 고치지 않는다. */\n const flagOf = (patch: RolePatch) => (patch.cuFlag || '').toUpperCase()\n\n for (const patch of patches.filter(patch => flagOf(patch) === '+')) {\n const name = patch.name?.trim()\n if (!name) {\n throw new Error(context.t('error.value is empty', { value: context.t('field.name') }))\n }\n\n await refuseRoleNameIfStanding({ name, domain, separately: patch.separately, tx })\n\n results.push(\n await roleRepository.save({\n domain,\n creator: user,\n updater: user,\n name,\n description: patch.description\n })\n )\n }\n\n for (const patch of patches.filter(patch => flagOf(patch) === 'M')) {\n const role = await roleRepository.findOne({ where: { domain: { id: domain.id }, id: patch.id } })\n\n /* 목록은 상속으로 부모 도메인 역할까지 보여 준다 — 그것을 고치려 하면 여기로 온다. */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be edited in that domain.'\n )\n }\n\n const name = patch.name?.trim()\n if (name && name !== role.name) {\n await refuseRoleNameIfStanding({ name, domain, separately: patch.separately, tx })\n\n role.name = name\n }\n\n if (patch.description !== undefined) {\n role.description = patch.description\n }\n\n results.push(await roleRepository.save({ ...role, updater: user }))\n }\n\n return results\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\")')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete role' })\n async deleteRole(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Boolean> {\n const { tx, domain } = context.state\n let role: Role = await tx\n .getRepository(Role)\n .findOne({ where: { domain: { id: domain.id }, id }, relations: ['users', 'privileges'] })\n\n /*\n * **세 가지 실패가 한 문장으로 나가고 있었다.** 아래 try 의 catch 가 무엇이 났든\n * 「사용 중이라 지울 수 없다」로 401 을 던졌다 — 못 찾은 것도(부모 도메인의 역할이 그렇다),\n * DB 가 실패한 것도 그렇게 말했다. 관리자는 있지도 않은 사용자를 떼러 갔다.\n *\n * 그래서 판단할 수 있는 둘을 먼저 가른다.\n */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be deleted in that domain.'\n )\n }\n\n refuseRolesInUse([role])\n\n try {\n role.privileges = []\n\n role = await tx.getRepository(Role).save(role)\n\n await tx.getRepository(Role).delete(role.id)\n\n /* `delete()` passes no subscriber - the deletion records itself, in the same transaction (ADR-0141 §4-1). */\n await recordAuditAction({ kind: 'role.delete', targetKind: 'Role', targetIds: [role.id], targetRole: 'handled' }, { manager: tx })\n\n return true\n } catch (e) {\n /* 여기까지 온 것은 지우는 동작 자체의 실패다 — 그 말을 그대로 올린다. */\n context.throw(500, e.message)\n }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple roles' })\n async deleteRoles(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext) {\n const { domain } = context.state\n\n /*\n * **도메인을 봤어야 했다.** 예전에는 받은 id 를 그대로 지웠다 — `user:mutation` 을 가진\n * 사람이 남의 도메인 역할을 id 만으로 지울 수 있었다. `deleteRole`(단수)은 처음부터\n * 도메인으로 좁혀 찾았으므로, 둘의 규칙이 달랐다.\n *\n * 그리고 **사용 중인 역할이 하나라도 있으면 아무것도 지우지 않고 그 이름으로 거절한다** — 단수\n * 쪽과 같은 규칙이다. 예전에는 사용 중인 것만 조용히 남기고 `true` 를 돌려주어, 관리자는\n * 무엇이 왜 안 지워졌는지 목록을 다시 보고 짐작해야 했다(2026-10-28 사용자: 「역할이 참조되는\n * 것이 있으면, 삭제가 안되어야하니」).\n */\n const roles = await getRepository(Role).find({\n where: { domain: { id: domain.id }, id: In(ids) },\n relations: ['users']\n })\n\n refuseRolesInUse(roles)\n\n const deletable = roles.map(role => role.id)\n if (deletable.length) {\n /* The deletion and its record stand or fall together (ADR-0141 §4-1). */\n await getRepository(Role).manager.transaction(async manager => {\n await manager.getRepository(Role).delete(deletable)\n await recordAuditAction({ kind: 'role.delete', targetKind: 'Role', targetIds: deletable, targetRole: 'handled' }, { manager })\n })\n }\n\n return true\n }\n}\n\n/**\n * A role someone holds is not deleted - the people holding it would lose what it opens without anyone deciding so.\n * One refusal for one role or many, naming each and how many hold it, so the screen can say what to take off first.\n */\nfunction refuseRolesInUse(roles: Role[]) {\n const held = roles.filter(role => role.users?.length)\n if (!held.length) return\n\n const names = held.map(role => `${role.name} (${role.users.length})`).join(', ')\n throw new Refusal('role-in-use', { names }, `These roles are held by users and are currently in use: ${names}. Take them off the users first.`)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-privilege-query.js","sourceRoot":"","sources":["../../../server/service/role/role-privilege-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA4D;AAE5D,oFAA6E;AAC7E,mDAA+C;AAE/C;;;;;;;;;GASG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAMvB,AAAN,KAAK,CAAC,UAAU,CAAS,aAA4B;QACnD,OAAO,IAAA,gDAAqB,EAAC,OAAO,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,CAAA;IACpE,CAAC;CACF,CAAA;AATY,gDAAkB;AAMvB;IALL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE;QAC9B,WAAW,EACT,iGAAiG;YACjG,gFAAgF;KACnF,CAAC;IACgB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAgB,6BAAa;;oDAEpD;6BARU,kBAAkB;IAD9B,IAAA,uBAAQ,EAAC,6BAAa,CAAC;GACX,kBAAkB,CAS9B","sourcesContent":["import { FieldResolver, Resolver, Root } from 'type-graphql'\n\nimport { isDeprecatedPrivilege } from '../privilege/privilege-deprecation.js'\nimport { RolePrivilege } from './role-types.js'\n\n/**\n * `rolePrivileges` 가 돌려주는 행에도 **유령 여부**를 붙인다.\n *\n * `Privilege` 타입에는 이미 같은 칸이 있는데(`privilege-query.ts`), `RolePrivilege` 는 다른\n * 타입이므로 그 칸을 못 쓴다. 권한 편집 화면이 드는 것은 이쪽이고, 유령을 못 보면 관리자는\n * **붙여도 아무 일이 없는 권한**을 계속 붙인다.\n *\n * 저장하지 않고 매번 계산하는 이유는 `privilege-deprecation.ts` 에 적혀 있다 — 뜻은 「영원히\n * 죽었다」가 아니라 **「지금 이 배포가 요구하지 않는다」**다.\n */\n@Resolver(RolePrivilege)\nexport class RolePrivilegeQuery {\n @FieldResolver(type => Boolean, {\n description:\n 'True when no module in this deployment declares this privilege anymore (@privilege directive). ' +\n 'Computed per request — it answers for the asking deployment, not for all time.'\n })\n async deprecated(@Root() rolePrivilege: RolePrivilege): Promise<boolean> {\n return isDeprecatedPrivilege(process['PRIVILEGES'], rolePrivilege)\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-query.js","sourceRoot":"","sources":["../../../server/service/role/role-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Int, Query, Resolver, Root } from 'type-graphql'\nimport { SelectQueryBuilder } from 'typeorm'\n\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\nimport { mayChangeParentRoles } from '../../utils/assignable-roles.js'\nimport { Role } from './role.js'\nimport { RoleList, RolePrivilege, UserRole, RoleBrief } from './role-types.js'\n\n@Resolver(Role)\nexport class RoleQuery {\n /*\n * A role by name, from this domain or the one it inherits from.\n *\n * ── Why this is two lookups and not one `In(...)` ───────────────────────\n * A name can match twice: the four duplicate checks in `role-mutation.ts` ask only about\n * this domain, so a role may be created with the same name as one that is already visible\n * through the parent. Measured 2026-09-20 — `createRole` accepts it, and both rows then\n * come back from every list.\n *\n * With `In([own, parent])` there is no `ORDER BY`, so which of the two answered was whatever\n * the driver returned first. Not wrong on a particular day, just unruled: the same call could\n * answer the parent's role on one installation and this domain's on another, and a role is\n * what a privilege check reads.\n *\n * This domain's own comes first. Both readings of what a duplicate should mean point the same\n * way — if the platform ends up forbidding the name, the rows that already exist still have\n * to resolve somewhere, and if shadowing becomes the official meaning this is what it means.\n * ⚠ It is **not** a ruling on that (ADR-0062 decision 3, still with the architect). It is the\n * deterministic reading of either.\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => Role, { description: 'To fetch role' })\n async role(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain } = context.state\n const repository = getRepository(Role)\n\n const own = await repository.findOneBy({ domain: { id: domain.id }, name })\n\n if (own || !domain.parentId) {\n return own\n }\n\n return await repository.findOneBy({ domain: { id: domain.parentId }, name })\n }\n\n /** Open by design: whether the caller may give or take the parent domain's roles here - the user screen locks them otherwise. */\n @Query(returns => Boolean, { description: \"Whether the caller may give or take the parent domain's roles from this domain.\" })\n async mayChangeParentRoles(@Ctx() context: ResolverContext): Promise<boolean> {\n return await mayChangeParentRoles(context.state.user, context.state.domain)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => RoleList, { description: 'To fetch multiple users' })\n async roles(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<RoleList> {\n const { domain } = context.state\n\n const [items, total] = await getQueryBuilderFromListParams({\n repository: getRepository(Role),\n params,\n domain,\n searchables: ['name', 'description']\n }).getManyAndCount()\n\n return { items, total }\n }\n\n /**\n * 한 역할의 권한 — **전체 목록과 이 역할이 가진 것을 한 번에** 돌려준다.\n *\n * 권한 편집 화면이 필요한 것은 「무엇이 있나」와 「이 역할이 무엇을 갖나」 둘인데, 그것을\n * 질의 둘로 가르면 화면이 둘을 짝지어야 하고 그 짝짓기가 역할 **이름**으로 이루어졌다\n * (옛 `role-privilege-editor` — 같은 이름의 부모 도메인 역할과 헷갈렸다). 여기서 `roleId` 로\n * 짝지어 주면 화면이 그 일을 안 한다.\n *\n * ── 원시 SQL 을 걷어냈다 ────────────────────────────────────────────────────\n * 셋을 동시에 잃고 있었다.\n *\n * ① `roleId` 를 문자열로 이어 붙였다. `user:query` 를 가진 사람이 임의의 SQL 을 넣을 수 있다.\n * ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다. 이 저장소는\n * DB 5종을 지원한다.\n * ③ 도메인을 안 봤다. 남의 도메인 역할의 `roleId` 로도 답했다.\n *\n * 권한 마스터는 전체 배포에 84행 규모(도메인마다 늘지 않는다)이므로 관계를 그대로 읽는다.\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [RolePrivilege], { description: 'To fetch privileges of a role' })\n async rolePrivileges(@Arg('roleId') roleId: string, @Ctx() context: ResolverContext): Promise<RolePrivilege[]> {\n const { domain } = context.state\n\n /* 상속으로 보이는 부모 도메인 역할까지가 범위다 — `role` 질의와 같은 규칙. */\n const role = await getRepository(Role).findOne({\n where: {\n id: roleId,\n domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }\n },\n relations: ['privileges']\n })\n\n if (!role) {\n return []\n }\n\n const assigned = new Set((role.privileges || []).map(privilege => privilege.id))\n\n return (await getRepository(Privilege).find()).map(({ id, name, category, description }) => ({\n id,\n name,\n category,\n description,\n assigned: assigned.has(id)\n }))\n }\n\n /**\n * 한 사람의 역할 — **부여할 수 있는 전체와 이 사람이 가진 것을 한 번에.**\n *\n * `rolePrivileges` 와 같은 모양이고, **같은 결함을 갖고 있었다.** 한 자리를 고쳤으면 두 번째\n * 자리도 재야 한다는 것이 이 파일에서 그대로 났다.\n *\n * ① `UR.users_id` 자리에 `userId` 를 문자열로 이어 붙였다. `user:query` 를 가진\n * 사람이 임의의 SQL 을 넣을 수 있다.\n * ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다(DB 5종 지원).\n * ③ `FROM roles` — **도메인을 안 봤다.** 남의 도메인 역할까지 전부 돌려줬다.\n *\n * ③ 을 고치면서 범위를 `role`·`rolePrivileges` 와 같게 뒀다 — 내 도메인과 부모 1단.\n * 그것이 실제로 부여할 수 있는 범위다.\n *\n * 지금 이 질의를 부르는 화면은 없다(옛 `user-role-editor` 는 `user(email:)` 로 읽었다).\n * 그래서 범위를 좁히는 것이 깨뜨릴 소비처가 없다.\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [UserRole], { description: 'To fetch roles of a user' })\n async userRoles(@Arg('userId') userId: string, @Ctx() context: ResolverContext): Promise<UserRole[]> {\n const { domain } = context.state\n\n const user = await getRepository(User).findOne({ where: { id: userId }, relations: ['roles'] })\n const assigned = new Set((user?.roles || []).map(role => role.id))\n\n const roles = await getRepository(Role).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) } }\n })\n\n return roles.map(({ id, name, description }) => ({ id, name, description, assigned: assigned.has(id) }))\n }\n\n /* The caller's own roles - the profile shows them with the work each holds (`RoleBrief.taskCoverage`, ADR-0139 §11-11). */\n @Query(returns => [RoleBrief], { description: 'To fetch roles of current user (id, name and description)' })\n async myRoles(@Ctx() context: ResolverContext): Promise<RoleBrief[]> {\n const { user, domain } = context.state\n\n const roles = await getRepository(Role)\n .createQueryBuilder('ROLE')\n .leftJoin('ROLE.users', 'USER')\n .leftJoin('ROLE.domain', 'ROLE_DOMAIN')\n .where('USER.id = :userId', { userId: user.id })\n .andWhere('ROLE_DOMAIN.id In(:...domainIds)', { domainIds: [domain.id, domain.parentId].filter(Boolean) })\n .select(['ROLE.id', 'ROLE.name', 'ROLE.description'])\n .getMany()\n\n return roles\n }\n\n /*\n * `checkRolePreset` stood here and has been removed. It read `config.get('rolePreset')`\n * and returned it unchanged, declared as `[Role]`.\n *\n * Nothing set that config key and nothing called the query, across every repository on\n * this machine — and it carried no `@privilege`, so any signed-in user could read\n * whatever an installation later put there. Empty today, quietly public the day someone\n * fills it. Role templates (`service/role-template/`) do what this was reaching for, as\n * a boot registry rather than a config blob.\n *\n * auth-base ships to npm, so consumers outside this repository cannot be counted. The\n * removal is recorded in the changelog — that is the only channel they have.\n */\n\n @FieldResolver(type => Domain)\n async domain(@Root() role: Role) {\n return await getRepository(Domain).findOneBy({ id: role.domainId })\n }\n\n /** Role의 users 필드를 위한 리졸버 - 사용자 조회 권한은 `Role.users` 선언에 있다(role.ts). */\n @FieldResolver(type => [User])\n async users(@Root() role: Role, @Ctx() context: ResolverContext) {\n const roleData = await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['users']\n })\n\n return roleData?.users || []\n }\n\n /**\n * 이 역할이 여는 권한이 **몇 개인가.**\n *\n * ── 왜 수만 따로 있나 ──────────────────────────────────────────────────────\n * 목록 화면이 「이 역할이 무엇을 여나」를 말해야 하는데, 그것을 위해 권한 **행**을 받고 있었다.\n *\n * ```\n * [확인함] roles(limit:20) 권한 포함 5,731 bytes · 69ms\n * [확인함] roles(limit:20) 권한 없이 518 bytes · 7ms → 11.1배 · 10배\n * ```\n *\n * 역할 2개에 권한 행 60개를 받아서 화면에는 잘린 코드 용어 셋을 그렸다. 역할 40개 ×\n * 권한 100개면 한 페이지에 4,000행이다. **`privileges` 필드 리졸버가 역할마다 한 번씩 도는\n * 것**까지 함께 없어지는 것이 더 큰 쪽이다.\n *\n * 그래서 목록은 이 칸을 쓴다 — 행을 안 받고 수를 받는다. 권한을 실제로 고치는 화면은\n * `rolePrivileges(roleId:)` 로 따로 읽는다(전체 목록 + 이 역할이 가진 것을 한 번에).\n *\n * ── 왜 관계를 세지 않고 질의로 세나 ────────────────────────────────────────\n * `relation(...).loadMany()` 는 행을 다 읽고 길이를 재는 것이라 여기서 고치려는 것을 그대로\n * 다시 한다. `getCount()` 는 DB 가 센다.\n *\n * 조인 테이블의 주인은 `Privilege` 쪽이지만(`privilege.ts:72` 의 `@JoinTable`), 양쪽이 관계를\n * 선언하고 있으므로 어느 쪽에서 조인해도 같다.\n */\n @FieldResolver(type => Int)\n async privilegeCount(@Root() role: Role): Promise<number> {\n return await getRepository(Privilege)\n .createQueryBuilder('PRIVILEGE')\n .innerJoin('PRIVILEGE.roles', 'ROLE')\n .where('ROLE.id = :id', { id: role.id })\n .getCount()\n }\n\n /**\n * 이 역할을 가진 사용자가 **몇 명인가.** 가진 사람이 있는 역할은 지우지 않으므로(`deleteRoles`),\n * 목록이 지우기 전에 그것을 보인다. `privilegeCount` 와 같이 DB 가 센다.\n */\n @FieldResolver(type => Int, { description: 'How many users hold this role - a role held by anyone is not deleted' })\n async userCount(@Root() role: Role): Promise<number> {\n return await getRepository(User)\n .createQueryBuilder('USER')\n .innerJoin('USER.roles', 'ROLE')\n .where('ROLE.id = :id', { id: role.id })\n .getCount()\n }\n\n @FieldResolver(type => [Privilege])\n async privileges(@Root() role: Role) {\n return (\n await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['privileges']\n })\n ).privileges\n }\n\n @FieldResolver(type => User)\n async updater(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.creatorId })\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-types.js","sourceRoot":"","sources":["../../../server/service/role/role-types.ts"],"names":[],"mappings":";;;;AAAA,+CAAgE;AAChE,iDAAiD;AACjD,uCAAgC;AAGzB,IAAM,OAAO,GAAb,MAAM,OAAO;CAqBnB,CAAA;AArBY,0BAAO;AAElB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;;qCACxC;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;4CACtD;AAGpB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;;sCAClF;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;;2CACjF;AASxB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;;2CAC5F;kBApBT,OAAO;IADnB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;GAChD,OAAO,CAqBnB;AAGM,IAAM,SAAS,GAAf,MAAM,SAAS;CA+BrB,CAAA;AA/BY,8BAAS;AAEpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;qCAC5E;AASX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;;6CAC5F;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uCACxD;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;8CACxD;AAIpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6EAA6E,EAAE,CAAC;;6CACnG;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;;wCAC5F;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;6CAC3F;AAGxB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;;yCACrG;oBA9BJ,SAAS;IADrB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;GACjE,SAAS,CA+BrB;AAGM,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAMpB,CAAA;AANY,4BAAQ;AAEnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,cAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;;uCACpE;AAGd;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uCACpE;mBALH,QAAQ;IADpB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;GAC7C,QAAQ,CAMpB;AAGM,IAAM,SAAS,GAAf,MAAM,SAAS;CASrB,CAAA;AATY,8BAAS;AAEpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;qCAClE;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;uCACnD;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;;8CAC7E;oBART,SAAS;IADrB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;GAC3E,SAAS,CASrB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAezB,CAAA;AAfY,sCAAa;AAExB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;;yCACvE;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;2CACxD;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;;+CACxD;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;kDACtD;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;;+CACjF;wBAdP,aAAa;IADzB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;GACxF,aAAa,CAezB;AAGM,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAYpB,CAAA;AAZY,4BAAQ;AAEnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;oCAClE;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;sCACnD;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;6CACjD;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;0CAC5E;mBAXP,QAAQ;IADpB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,kEAAkE,EAAE,CAAC;GACnF,QAAQ,CAYpB","sourcesContent":["import { ObjectType, InputType, Field, Int } from 'type-graphql'\nimport { ObjectRef } from '@things-factory/shell'\nimport { Role } from './role.js'\n\n@InputType({ description: 'Input for creating a new role.' })\nexport class NewRole {\n @Field({ description: 'The name of the new role.' })\n name: string\n\n @Field({ nullable: true, description: 'A description for the new role.' })\n description?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Users to be assigned to the new role.' })\n users?: ObjectRef[]\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Privileges to be granted by the new role.' })\n privileges?: ObjectRef[]\n\n /*\n * The operator saying this domain stands its own role of a name it already inherits\n * (ADR-0062 decision 3). The name stays the same — that is the point of it, because moving a\n * domain off an inherited structure means standing up the same names here first (decision 2).\n * Absent means not said, and an inherited name is refused.\n */\n @Field({ nullable: true, description: \"Stand this role even though the domain inherits one of the same name.\" })\n separately?: boolean\n}\n\n@InputType({ description: 'Input for updating (patching) an existing role.' })\nexport class RolePatch {\n @Field({ nullable: true, description: 'The unique identifier of the role to update.' })\n id?: string\n\n /*\n * The operator saying this domain stands its own role of a name it already inherits\n * (ADR-0062 decision 3). The name stays the same — that is the point of it, because moving a\n * domain off an inherited structure means standing up the same names here first (decision 2).\n * Absent means not said, and an inherited name is refused.\n */\n @Field({ nullable: true, description: \"Stand this role even though the domain inherits one of the same name.\" })\n separately?: boolean\n\n @Field({ nullable: true, description: 'The new name for the role.' })\n name?: string\n\n @Field({ nullable: true, description: 'The new description for the role.' })\n description?: string\n\n /* `updateRole` only: null clears it. The grid's `updateMultipleRole` edits names and descriptions and ignores this. */\n @Field({ nullable: true, description: 'The id of the role template this role is based on; null to base it on none.' })\n templateId?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'A new list of users to be assigned to the role.' })\n users?: ObjectRef[]\n\n @Field(type => [ObjectRef], { nullable: true, description: 'A new list of privileges to be granted by the role.' })\n privileges?: ObjectRef[]\n\n @Field({ nullable: true, description: 'A flag indicating whether the role is being created, updated, or deleted.' })\n cuFlag?: string\n}\n\n@ObjectType({ description: 'A paginated list of roles.' })\nexport class RoleList {\n @Field(type => [Role], { nullable: true, description: 'The list of role items.' })\n items?: Role[]\n\n @Field(type => Int, { nullable: true, description: 'The total number of roles.' })\n total?: number\n}\n\n@ObjectType({ description: 'A brief summary of a role: its ID, name and description.' })\nexport class RoleBrief {\n @Field({ nullable: true, description: 'The unique identifier of the role.' })\n id?: string\n\n @Field({ nullable: true, description: 'The name of the role.' })\n name?: string\n\n @Field({ nullable: true, description: 'What the role is for, in the words of whoever made it.' })\n description?: string\n}\n\n@ObjectType({ description: 'Represents a privilege and whether it is assigned to a specific role.' })\nexport class RolePrivilege {\n @Field({ nullable: true, description: 'The unique identifier of the privilege.' })\n id?: string\n\n @Field({ nullable: true, description: 'The name of the privilege.' })\n name?: string\n\n @Field({ nullable: true, description: 'The category of the privilege.' })\n category?: string\n\n @Field({ nullable: true, description: 'A description of the privilege.' })\n description?: string\n\n @Field({ nullable: true, description: 'Indicates whether the privilege is assigned to the role.' })\n assigned?: boolean\n}\n\n@ObjectType({ description: 'Represents a role and whether it is assigned to a specific user.' })\nexport class UserRole {\n @Field({ nullable: true, description: 'The unique identifier of the role.' })\n id?: string\n\n @Field({ nullable: true, description: 'The name of the role.' })\n name?: string\n\n @Field({ nullable: true, description: 'A description of the role.' })\n description?: string\n\n @Field({ nullable: true, description: 'Indicates whether the role is assigned to the user.' })\n assigned?: boolean\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role.js","sourceRoot":"","sources":["../../../server/service/role/role.ts"],"names":[],"mappings":"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","sourcesContent":["import { Domain } from '@things-factory/shell'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n JoinTable,\n ManyToMany,\n ManyToOne,\n RelationId,\n PrimaryGeneratedColumn,\n UpdateDateColumn\n} from 'typeorm'\nimport { Directive, ObjectType, Field, ID } from 'type-graphql'\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\n\n@Entity()\n@Index('ix_role_0', (role: Role) => [role.name, role.domain], { unique: true })\n@ObjectType({ description: 'A role that groups a set of privileges, which can be assigned to users.' })\nexport class Role {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the role.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain to which the role belongs.' })\n domain?: Domain\n\n @RelationId((role: Role) => role.domain)\n domainId: string\n\n @Column()\n @Field({ nullable: true, description: 'The name of the role.' })\n name: string\n\n /*\n * Who holds a role is the user admin's: reading roles does not list their people. The directive sits on the\n * declaration - type-graphql drops a field resolver's directive when the type declares the field (ADR-0139), and\n * until 2026-10-18 the one on RoleQuery.users guarded nothing.\n */\n @ManyToMany(type => User, user => user.roles)\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Field(type => [User], { nullable: true, description: 'Users who are assigned this role.' })\n users: User[]\n\n @ManyToMany(type => Privilege, privilege => privilege.roles)\n @Field(type => [Privilege], { nullable: true, description: 'Privileges granted by this role.' })\n privileges: Privilege[]\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'A description of the role.' })\n description: string\n\n /**\n * The role template this role is based on - its declaration id (`office.finance`), one at most (ADR-0075 decision 1,\n * amended 2026-10-26). The role list counts what the role lacks against it and the role screen lays the role against it.\n *\n * It is stored because the name cannot say it: a role named in the site's own words (경영지원) has no template of\n * that name, and the role list never told anyone it had fallen behind. It is not a foreign key - a template is a\n * declaration in code, not a row. An id no installed app declares any more stays here and the role screen says so;\n * nothing clears it on its own.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The id of the role template this role is based on; null when it is based on none.' })\n templateId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who created the role.' })\n creator: User\n\n @RelationId((role: Role) => role.creator)\n creatorId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who last updated the role.' })\n updater: User\n\n @RelationId((role: Role) => role.updater)\n updaterId: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the role was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the role was last updated.' })\n updatedAt: Date\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/role-template/index.ts"],"names":[],"mappings":";;;AAAA,mFAAgG;AAChG,2EAAkE;AAClE,qEAA4D;AAE5D,sFAAsF;AACzE,QAAA,QAAQ,GAAG,EAAE,CAAA;AACb,QAAA,SAAS,GAAG,CAAC,0CAAiB,EAAE,gDAAoB,EAAE,uDAAuB,EAAE,qDAAqB,CAAC,CAAA","sourcesContent":["import { RoleBriefTaskResolver, RoleTemplateGapResolver } from './role-template-gap-resolver.js'\nimport { RoleTemplateMutation } from './role-template-mutation.js'\nimport { RoleTemplateQuery } from './role-template-query.js'\n\n/* Templates live in the boot registry, not in a table, so there is no entity here. */\nexport const entities = []\nexport const resolvers = [RoleTemplateQuery, RoleTemplateMutation, RoleTemplateGapResolver, RoleBriefTaskResolver]\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-task.js","sourceRoot":"","sources":["../../../server/service/role-template/role-task.ts"],"names":[],"mappings":";;AAgDA,4CA8BC;AAED,8BAEC;AAED,oCAEC;AAGD,wCAEC;AAMD,kCAYC;AAGD,oCAEC;AAGD,oDAEC;AAMD,oCAKC;AAGD,4CAEC;AAwBD,wCAiCC;AAhMD,6CAA4C;AAkC5C,MAAM,QAAQ,GAAG,YAAY,CAAA;AAE7B,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAA;AAEtB,SAAS,QAAQ;IACf,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAA;AAC1B,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,IAAc;IAC7C,MAAM,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAEtC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,sFAAsF,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,CAAC,CAAA;IAClI,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,EAAE,2GAA2G,CAAC,CAAA;IACnJ,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,EAAE,sFAAsF,CAAC,CAAA;QAC9H,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,EAAE,sBAAsB,CAAC,CAAA;IAC9D,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAClF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAE1E,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,YAAM,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,EAAE,6DAA6D,CAAC,CAAA;IACnG,CAAC;IAED,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAA;AAC/C,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AAClC,CAAC;AAED,SAAgB,YAAY,CAAC,EAAU;IACrC,OAAO,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;AACvB,CAAC;AAED,kFAAkF;AAClF,SAAgB,cAAc,CAAC,IAAc;IAC3C,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAA4B;IACtD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAA;IAE1C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3C,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED,mFAAmF;AACnF,SAAgB,YAAY,CAAC,QAAgB,EAAE,IAAY;IACzD,OAAO,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAA;AAC9B,CAAC;AAED,mHAAmH;AACnH,SAAgB,oBAAoB,CAAC,IAAc,EAAE,WAAoC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE;IAClH,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC,CAAA;AACxJ,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,QAAgB,EAAE,IAAY,EAAE,QAAoB,SAAS,EAAE;IAC1F,MAAM,IAAI,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACnG,OAAO,KAAK;SACT,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;SACnG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,+EAA+E;AAC/E,SAAgB,gBAAgB,CAAC,IAAc,EAAE,WAAoC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE;IAC9G,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;AAC9H,CAAC;AAcD;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAC5B,IAAyB,EACzB,QAAoB,SAAS,EAAE,EAC/B,WAAoC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE;IAE/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAE/B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,IAAiD,EAAE,EAAE,CACjE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAE9J,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAEvG,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACpF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACjI,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QAEpI,MAAM,MAAM,GAAmB,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;QAEtH,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IACpG,CAAC,CAAC,CAAA;IAEF,MAAM,YAAY,GAAG,WAAW,CAAC;QAC/B,CAAC,GAAG,IAAI,CAAC;aACN,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;aAC/C,IAAI,EAAE;aACN,GAAG,CAAC,GAAG,CAAC,EAAE;YACT,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YACvC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAA;QACnC,CAAC,CAAC;KACL,CAAC,CAAA;IAEF,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;AAC1C,CAAC","sourcesContent":["import { logger } from '@things-factory/env'\n\nimport type { RoleTemplateGrant } from './role-template.js'\n\n/**\n * Role tasks — a piece of work named by the module that owns it, and the privileges it takes (ADR-0139 decision 11).\n *\n * ── Why a task and not a template ────────────────────────────────────────────\n * A template is a seat in one application: who does what in an office or a plant. A module does not know the seats\n * of the applications that install it, but it does know its own work — accounting knows that writing an expenditure\n * request takes one's own requests and picking a counterparty and an account subject. So the module names the work\n * and the application builds its seats from the work (`RoleTemplate.tasks`). An application names work of its own\n * in the same form, under its own prefix.\n *\n * ── What a task is not ───────────────────────────────────────────────────────\n * - Not a narrower door. A task holds whole privileges, and each privilege opens what its doors open. A task called\n * \"read the books\" that holds `accounting:query` opens everything `accounting:query` opens; the name and the\n * description say so rather than hide it.\n * - Not stored. Nothing on a role records which tasks it was built from — the role screen compares a role's\n * privileges with the tasks, computed, the same way a template's gap is (`templateGapOf`).\n * - Not nested. A task lists privileges only; seats are where tasks are put together.\n * - Not declared twice. One piece of work has one task. Two tasks may share a privilege — picking a counterparty is\n * part of more than one piece of work — and that is not a duplicate.\n *\n * Its words live in the declaring module's translations, `task.<id>.name` and `task.<id>.description`, so a module\n * that is not installed takes its words away with it. The screen draws the id when there are none.\n */\nexport type RoleTask = {\n /** `<module>.<work>` — the declaring module's name first: `accounting.expense-authoring`. */\n id: string\n /** Privileges by category, axes listed one by one — the shape a template's grants have. */\n grants: RoleTemplateGrant[]\n}\n\nconst REGISTRY = 'ROLE_TASKS'\n\nprocess[REGISTRY] = {}\n\nfunction registry(): Record<string, RoleTask> {\n return process[REGISTRY]\n}\n\n/**\n * Declares a task. Call it from the module's server entry, beside its `@privilege` doors.\n *\n * Privileges are not checked here, for the reason templates are not: modules load before the schema is built and\n * `process['PRIVILEGES']` is filled. Boot checks them (`reportRoleDefinitionErrors`).\n */\nexport function registerRoleTask(task: RoleTask): void {\n const prefix = task?.id?.split('.')[0]\n\n if (!task?.id || !prefix || prefix === task.id) {\n throw new Error(`A role task needs an id of the form <module>.<work>, the declaring module first — \"${task?.id ?? ''}\" is not.`)\n }\n\n if (!Array.isArray(task.grants) || task.grants.length === 0) {\n throw new Error(`Role task \"${task.id}\" holds no privileges. A task is the privileges a piece of work takes; one that takes none is not a task.`)\n }\n\n for (const grant of task.grants) {\n if (!grant?.category || !Array.isArray(grant.axes) || grant.axes.length === 0) {\n throw new Error(`Role task \"${task.id}\" has a grant without a category or with no axes. Axes are always listed one by one.`)\n }\n }\n\n if (registry()[task.id]) {\n throw new Error(`Role task \"${task.id}\" is declared twice.`)\n }\n\n const grants = mergeGrants([task.grants])\n const written = task.grants.reduce((count, grant) => count + grant.axes.length, 0)\n const kept = grants.reduce((count, grant) => count + grant.axes.length, 0)\n\n if (kept !== written) {\n logger.warn(`[role-task] \"${task.id}\" names the same privilege more than once; it is held once.`)\n }\n\n registry()[task.id] = { id: task.id, grants }\n}\n\nexport function roleTasks(): RoleTask[] {\n return Object.values(registry())\n}\n\nexport function roleTaskById(id: string): RoleTask | undefined {\n return registry()[id]\n}\n\n/** The module a task is declared by — the part of its id before the first dot. */\nexport function roleTaskModule(task: RoleTask): string {\n return task.id.split('.')[0]\n}\n\n/**\n * Several lists of grants as one: one entry per category, each axis once, in the order first written.\n * A privilege held through two tasks, or through a task and a direct grant, is held once.\n */\nexport function mergeGrants(lists: RoleTemplateGrant[][]): RoleTemplateGrant[] {\n const axesOf = new Map<string, string[]>()\n\n for (const grant of lists.flat()) {\n const axes = axesOf.get(grant.category) || []\n for (const axis of grant.axes) {\n if (!axes.includes(axis)) axes.push(axis)\n }\n axesOf.set(grant.category, axes)\n }\n\n return [...axesOf.entries()].map(([category, axes]) => ({ category, axes }))\n}\n\n/** `category axis` — the key `process['PRIVILEGES']` and a role's held set use. */\nexport function privilegeKey(category: string, axis: string): string {\n return `${category} ${axis}`\n}\n\n/** A task's privileges this installation does not declare, as `category:axis` — a definition error of the task. */\nexport function undeclaredTaskGrants(task: RoleTask, declared: Record<string, unknown> = process['PRIVILEGES'] || {}): string[] {\n return task.grants.flatMap(grant => grant.axes.filter(axis => !declared[privilegeKey(grant.category, axis)]).map(axis => `${grant.category}:${axis}`))\n}\n\n/**\n * The tasks that hold one privilege, fewest privileges first, ties by id - what a person refused that privilege may\n * ask for (ADR-0103 decision 9). The same order wherever a task is offered for a privilege.\n */\nexport function tasksHolding(category: string, axis: string, tasks: RoleTask[] = roleTasks()): RoleTask[] {\n const size = (task: RoleTask) => task.grants.reduce((count, grant) => count + grant.axes.length, 0)\n return tasks\n .filter(task => task.grants.some(grant => grant.category === category && grant.axes.includes(axis)))\n .sort((a, b) => size(a) - size(b) || a.id.localeCompare(b.id))\n}\n\n/** A task's privileges this installation declares, as `category axis` keys. */\nexport function declaredTaskKeys(task: RoleTask, declared: Record<string, unknown> = process['PRIVILEGES'] || {}): string[] {\n return task.grants.flatMap(grant => grant.axes.map(axis => privilegeKey(grant.category, axis))).filter(key => declared[key])\n}\n\nexport type RoleTaskStatus = 'all' | 'some' | 'none' | 'broken'\n\nexport type RoleTaskCoverage = {\n taskId: string\n module: string\n status: RoleTaskStatus\n held: RoleTemplateGrant[]\n missing: RoleTemplateGrant[]\n /** Privileges the task names that this installation does not declare. Any of them makes the task `broken`. */\n undeclared: RoleTemplateGrant[]\n}\n\n/**\n * **A role's privileges laid against every task** — what the role screen groups by.\n *\n * It says which of a task's privileges the role holds, nothing more. `all` is not \"this person can do the work\":\n * another role of theirs, the data's own scope, an approval line or a field's door decide that too. `some` is not a\n * fault: a privilege shared by several tasks makes each of them `some`. `none` is the common case.\n *\n * `unclassified` is what the role holds, declared here, that no task names. A privilege the role holds that nothing\n * declares any more is not here — the screen already strikes it through (`isDeprecatedPrivilege`).\n */\nexport function taskCoverageOf(\n held: ReadonlySet<string>,\n tasks: RoleTask[] = roleTasks(),\n declared: Record<string, unknown> = process['PRIVILEGES'] || {}\n): { tasks: RoleTaskCoverage[]; unclassified: RoleTemplateGrant[] } {\n const named = new Set<string>()\n\n const coverage = tasks.map(task => {\n const pick = (keep: (category: string, axis: string) => boolean) =>\n task.grants.map(grant => ({ category: grant.category, axes: grant.axes.filter(axis => keep(grant.category, axis)) })).filter(grant => grant.axes.length > 0)\n\n task.grants.forEach(grant => grant.axes.forEach(axis => named.add(privilegeKey(grant.category, axis))))\n\n const undeclared = pick((category, axis) => !declared[privilegeKey(category, axis)])\n const holds = pick((category, axis) => Boolean(declared[privilegeKey(category, axis)]) && held.has(privilegeKey(category, axis)))\n const missing = pick((category, axis) => Boolean(declared[privilegeKey(category, axis)]) && !held.has(privilegeKey(category, axis)))\n\n const status: RoleTaskStatus = undeclared.length ? 'broken' : !missing.length ? 'all' : holds.length ? 'some' : 'none'\n\n return { taskId: task.id, module: roleTaskModule(task), status, held: holds, missing, undeclared }\n })\n\n const unclassified = mergeGrants([\n [...held]\n .filter(key => declared[key] && !named.has(key))\n .sort()\n .map(key => {\n const [category, axis] = key.split(' ')\n return { category, axes: [axis] }\n })\n ])\n\n return { tasks: coverage, unclassified }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-template-gap-resolver.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-gap-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAA4D;AAE5D,iDAAqD;AAErD,4DAAqD;AACrD,6CAAsC;AACtC,yDAAiD;AACjD,yDAAkD;AAClD,iDAA+C;AAC/C,qEAAwF;AAExF;;;;;;;GAOG;AAEI,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAM5B,AAAN,KAAK,CAAC,WAAW,CAAS,IAAU;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,IAAA,gCAAa,EAAC,IAAI,CAAC,UAAU,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,CAAC;IAKK,AAAN,KAAK,CAAC,YAAY,CAAS,IAAU;QACnC,OAAO,IAAA,6BAAc,EAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3C,CAAC;CACF,CAAA;AApBY,0DAAuB;AAM5B;IALL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,4CAAmB,EAAE;QAC1C,QAAQ,EAAE,IAAI;QACd,WAAW,EACT,sMAAsM;KACzM,CAAC;IACiB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAO,cAAI;;0DAMnC;AAKK;IAHL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,iDAAwB,EAAE;QAC/C,WAAW,EAAE,oFAAoF;KAClG,CAAC;IACkB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAO,cAAI;;2DAEpC;kCAnBU,uBAAuB;IADnC,IAAA,uBAAQ,EAAC,cAAI,CAAC;GACF,uBAAuB,CAoBnC;AAED;;;GAGG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAI1B,AAAN,KAAK,CAAC,YAAY,CAAS,IAAe;QACxC,OAAO,IAAA,6BAAc,EAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3C,CAAC;CACF,CAAA;AAPY,sDAAqB;AAI1B;IAHL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,iDAAwB,EAAE;QAC/C,WAAW,EAAE,oFAAoF;KAClG,CAAC;IACkB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAO,yBAAS;;yDAEzC;gCANU,qBAAqB;IADjC,IAAA,uBAAQ,EAAC,yBAAS,CAAC;GACP,qBAAqB,CAOjC;AAED,KAAK,UAAU,MAAM,CAAC,IAAqB;IACzC,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,wBAAS,CAAC;SACxC,kBAAkB,CAAC,WAAW,CAAC;SAC/B,MAAM,CAAC,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;SAChD,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC;SACpC,KAAK,CAAC,eAAe,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;SACvC,OAAO,EAAE,CAAA;IAEZ,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;AAClF,CAAC","sourcesContent":["import { FieldResolver, Resolver, Root } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { Role } from '../role/role.js'\nimport { RoleBrief } from '../role/role-types.js'\nimport { templateGapOf } from './role-template.js'\nimport { taskCoverageOf } from './role-task.js'\nimport { RoleTaskCoverageListView, RoleTemplateGapView } from './role-template-types.js'\n\n/**\n * `Role.templateGap` and `Role.taskCoverage` - the role management screen lays a role against its base template and against\n * the tasks (ADR-0075 decision 1, ADR-0139 decision 11). The rules are `templateGapAgainst` and `taskCoverageOf`;\n * this only reads the role's privileges.\n *\n * Both are fields of a role, so they are reached only through a door that already returned the role - the same\n * privilege and the same domain the role list asks for. They add no door of their own.\n */\n@Resolver(Role)\nexport class RoleTemplateGapResolver {\n @FieldResolver(type => RoleTemplateGapView, {\n nullable: true,\n description:\n \"What the role's base template grants here that the role does not hold, and what the role holds beyond it. Null when the role has no base template, or its base is not declared in this installation.\"\n })\n async templateGap(@Root() role: Role): Promise<RoleTemplateGapView | null> {\n if (!role.templateId) {\n return null\n }\n\n return templateGapOf(role.templateId, await heldBy(role))\n }\n\n @FieldResolver(type => RoleTaskCoverageListView, {\n description: \"The role's privileges laid against every registered task, and those no task names.\"\n })\n async taskCoverage(@Root() role: Role): Promise<RoleTaskCoverageListView> {\n return taskCoverageOf(await heldBy(role))\n }\n}\n\n/**\n * `RoleBrief.taskCoverage` - the same reading for one's own roles (ADR-0139 §11-11): the profile says what work each role\n * of the caller holds. `myRoles` returns only the caller's roles, so this reaches no one else's.\n */\n@Resolver(RoleBrief)\nexport class RoleBriefTaskResolver {\n @FieldResolver(type => RoleTaskCoverageListView, {\n description: \"The role's privileges laid against every registered task, and those no task names.\"\n })\n async taskCoverage(@Root() role: RoleBrief): Promise<RoleTaskCoverageListView> {\n return taskCoverageOf(await heldBy(role))\n }\n}\n\nasync function heldBy(role: { id?: string }): Promise<Set<string>> {\n const held = await getRepository(Privilege)\n .createQueryBuilder('PRIVILEGE')\n .select(['PRIVILEGE.category', 'PRIVILEGE.name'])\n .innerJoin('PRIVILEGE.roles', 'ROLE')\n .where('ROLE.id = :id', { id: role.id })\n .getMany()\n\n return new Set(held.map(privilege => `${privilege.category} ${privilege.name}`))\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-template-mutation.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { getRepository, SpeakRefusals } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { refuseRoleNameIfStanding } from '../../utils/refuse-role-name.js'\nimport { Role } from '../role/role.js'\nimport { roleTemplateById, computedRoleTemplates, RegisteredRoleTemplate, templateDefinitionErrors, templateGrantsOf } from './role-template.js'\n\n/* `refuseRoleNameIfStanding` throws a `Refusal`; this is the exit that turns it into a sentence. */\n@Resolver(Role)\n@UseMiddleware(SpeakRefusals)\nexport class RoleTemplateMutation {\n /**\n * Creates a role from a template — once per name.\n *\n * ── Why an existing name is used rather than replaced ────────────────────────\n * Approval lines store a role's **id** (`worklist` resolves an approver of type `Role`\n * by id). Creating a second QUALITY-INSPECTOR would leave every approval routed at the\n * first one while the administrator edits the second, and nothing would report it: the\n * screen shows two roles and the approvals go to the one nobody is looking at.\n *\n * So an existing role of that name is used as it stands. operato-plant's seed already\n * behaves this way (`seed-demo-org.ts` looks the role up and only saves when absent);\n * this is the same rule, stated once.\n *\n * ── One base template ─────────────────────────────────────────────────────────\n * The role this makes or tops up is based on this template (`Role.templateId`, ADR-0075\n * decision 1 amended 2026-10-26). A role based on another template is refused, not topped\n * up: a role has one base, and the administrator changes it on the role screen first.\n *\n * ── Why privileges are added and never removed ───────────────────────────────\n * If a template later drops a privilege it stays on roles already seeded, because the seed\n * runs once and the role belongs to the administrator afterwards.\n *\n * A privilege that is no longer declared anywhere is already handled elsewhere and not\n * here — `isDeprecatedPrivilege` marks it, the role screen strikes it through and refuses\n * to switch it back on, and removing it is the administrator's call. Reclaiming it from\n * this side would mean overwriting, and would be unable to tell a stale grant from one\n * the administrator added on purpose.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Role, { description: 'To create a role from a role template' })\n async createRoleFromTemplate(\n @Arg('templateId') templateId: string,\n /**\n * What this plant calls the seat. Defaults to the template's name.\n *\n * The template knows which seat exists in a factory running this product; it does not\n * know what the people there call it. And because a role is also what an approval line\n * points at, the same seat is sometimes needed twice — a day shift and a night shift\n * inspector hold the same privileges and take different approvals. Without a name of\n * their own a template could be used exactly once per domain.\n */\n @Arg('name', { nullable: true }) name: string | undefined,\n @Ctx() context: ResolverContext,\n /**\n * The operator saying this domain stands its own role of a name it already inherits\n * (ADR-0062 decision 3).\n *\n * ⚠ This is where the rule bites hardest, and stopping here is the point of it. A template\n * seeds the seats a product needs, and on a tenant with a parent those names are often\n * already visible through inheritance — so the template would quietly put a second row of\n * the same name beside the inherited one, which is the state the whole rule exists to\n * prevent.\n *\n * The name is not changed to get past it. Standing up the same names in this domain is the\n * first step of moving off an inherited structure (decision 2), so this says \"yes, that is\n * what I am doing\" rather than \"call it something else\". `name` above remains for the other\n * thing — a second seat with the same privileges under a name of its own.\n */\n @Arg('separately', { nullable: true }) separately: boolean | undefined\n ): Promise<Role> {\n const { domain, user, tx } = context.state\n\n const template = findTemplate(templateId)\n\n if (!template) {\n throw new Error(`There is no role template \"${templateId}\" in this installation.`)\n }\n\n /*\n * A template whose tasks are not all there is refused whole, before a role is made or a privilege added\n * (ADR-0139 decision 11). Applying the rest would stand up a seat short of work its name says it does, and\n * the gap would look like the administrator's choice.\n */\n const errors = templateDefinitionErrors(template)\n if (errors.length) {\n throw new Error(`Role template \"${template.id}\" cannot be applied: ${errors.join('; ')}.`)\n }\n\n const roleName = (name || template.name).trim()\n\n if (!roleName) {\n throw new Error('A role needs a name.')\n }\n\n const roleRepository = tx.getRepository(Role)\n\n let role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, name: roleName },\n relations: ['privileges']\n })\n\n if (role?.templateId && role.templateId !== template.id) {\n throw new Error(\n `Role \"${roleName}\" is based on role template \"${role.templateId}\", not \"${template.id}\" - change its base template on the role screen first.`\n )\n }\n\n if (!role) {\n /*\n * Only on the way to creating one. Topping up an existing role of ours is not a new name\n * and must not be refused — running the same template twice is how a seat gets privileges\n * the template gained since.\n */\n await refuseRoleNameIfStanding({ name: roleName, domain, separately, tx })\n\n role = await roleRepository.save({\n domain,\n name: roleName,\n description: template.description,\n templateId: template.id,\n creator: user,\n updater: user\n } as any)\n\n role = await roleRepository.findOne({\n where: { id: role!.id },\n relations: ['privileges']\n })\n }\n\n const held = new Set((role!.privileges || []).map(privilege => `${privilege.category} ${privilege.name}`))\n const wanted = templateGrantsOf(template).flatMap(grant => grant.axes.map(axis => ({ category: grant.category, name: axis })))\n\n /*\n * Privileges have no domain — the whole server shares one set and what differs per\n * domain is only which role holds them (`Privilege` has no domain column; auth-base's\n * boot creates them by category and name alone). Filtering by domain finds nothing.\n */\n const privilegeRepository = tx.getRepository(Privilege)\n const added: Privilege[] = []\n\n for (const one of wanted) {\n if (held.has(`${one.category} ${one.name}`)) {\n continue\n }\n\n const privilege = await privilegeRepository.findOne({ where: { category: one.category, name: one.name } })\n\n /*\n * A privilege this installation does not declare is skipped, never created. Creating\n * it here would make a row no `@privilege` asks for: the role would look as if it\n * had been granted something, and open nothing. Granted-but-closed is the worst of\n * the three outcomes. `roleTemplates` reports these as `missingCount` before the\n * administrator picks the template.\n */\n if (privilege) {\n added.push(privilege)\n }\n }\n\n /*\n * The base template is recorded and nothing more. What an administrator needs later is \"these two\n * privileges were added to the template\", and that is the template's current grants against the role's\n * current privileges — computed from the base, not stored (`Role.templateGap`).\n */\n role!.privileges = [...(role!.privileges || []), ...added]\n role!.templateId = template.id\n role!.updater = user\n\n return await roleRepository.save(role!)\n }\n}\n\nfunction findTemplate(id: string): RegisteredRoleTemplate | undefined {\n return roleTemplateById(id) || computedRoleTemplates().find(template => template.id === id)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-template-query.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Role } from '../role/role.js'\nimport { allRoleTemplates, RegisteredRoleTemplate, templateDefinitionErrors, templateGrantsOf } from './role-template.js'\nimport { RoleTemplateView } from './role-template-types.js'\n\n@Resolver(RoleTemplateView)\nexport class RoleTemplateQuery {\n /**\n * The templates this domain can start a role from.\n *\n * Gated the same way role editing is: choosing a template creates a role, so whoever may\n * do this is whoever may create roles.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [RoleTemplateView], { description: 'To fetch the role templates this installation offers' })\n async roleTemplates(@Ctx() context: ResolverContext): Promise<RoleTemplateView[]> {\n const { domain } = context.state\n\n /*\n * Read the existing role names once. The alternative — a field resolver asking per\n * template — runs a query per row for an answer the list already has.\n */\n const existing = new Set(\n (\n await getRepository(Role)\n .createQueryBuilder('ROLE')\n .select('ROLE.name')\n .where('ROLE.domain = :domain', { domain: domain.id })\n .getMany()\n ).map(role => role.name)\n )\n\n return allRoleTemplates().map(template => view(template, existing))\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => RoleTemplateView, { nullable: true, description: 'To fetch one role template by id' })\n async roleTemplate(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<RoleTemplateView | null> {\n const templates = await this.roleTemplates(context)\n\n return templates.find(template => template.id === id) || null\n }\n}\n\nfunction view(template: RegisteredRoleTemplate, existing: Set<string>): RoleTemplateView {\n const declared = process['PRIVILEGES'] || {}\n\n const grants = templateGrantsOf(template).map(grant => {\n const present = grant.axes.filter(axis => declared[`${grant.category} ${axis}`])\n const missingAxes = grant.axes.filter(axis => !declared[`${grant.category} ${axis}`])\n\n return { category: grant.category, axes: present, missingAxes }\n })\n\n return {\n id: template.id,\n name: template.name,\n description: template.description,\n grants,\n tasks: template.tasks || [],\n definitionErrors: templateDefinitionErrors(template),\n privilegeCount: grants.reduce((count, grant) => count + grant.axes.length, 0),\n missingCount: grants.reduce((count, grant) => count + grant.missingAxes.length, 0),\n defaultNameTaken: existing.has(template.name),\n /*\n * Declared with no grants at all — an approval seat. Distinct from a template whose\n * grants all went missing in this installation, which is a defect and shows a\n * `missingCount` instead.\n */\n grantsNothingDeliberately: !template.tasks?.length && !template.grants?.length\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-template-types.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-types.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, Int, ObjectType } from 'type-graphql'\n\n/**\n * What a template looks like *to this installation*.\n *\n * The declared template and the offered template are not the same thing: an app declares\n * grants across modules it expects to be installed, and a given installation has a subset.\n * operato-plant declares 14 categories; the plant app running on :4000 sees 33 of the 47\n * categories declared across the repository. So the answer has to say which parts of the\n * template this installation can actually honour.\n */\n@ObjectType({ description: 'One module and the axes a role template grants on it.' })\nexport class RoleTemplateGrantView {\n @Field({ description: 'The privilege category — what is guarded.' })\n category: string\n\n @Field(type => [String], { description: 'The privilege axes granted on that category.' })\n axes: string[]\n\n /**\n * Axes this installation does not declare.\n *\n * Dropping them quietly would hand the administrator a role that does not match the\n * template's name, with no way to tell. They are reported instead, and the screen says\n * \"not present in this installation\".\n */\n @Field(type => [String], { description: 'Axes named by the template that this installation does not declare.' })\n missingAxes: string[]\n}\n\n@ObjectType({ description: 'A role template offered by this installation.' })\nexport class RoleTemplateView {\n @Field({ description: 'Declaration key, namespaced by the declaring application.' })\n id: string\n\n @Field({ description: 'The role name this template creates in a domain.' })\n name: string\n\n @Field({ nullable: true, description: 'A description of the seat this template creates.' })\n description?: string\n\n @Field(type => [RoleTemplateGrantView], { description: 'The modules and axes this template grants.' })\n grants: RoleTemplateGrantView[]\n\n @Field(type => [String], { description: 'The tasks this template is built from, by id - each a piece of work a module or application names.' })\n tasks: string[]\n\n /**\n * What stops this template from being applied at all — a task nobody registered, or a task naming a\n * privilege this installation does not declare (ADR-0139 decision 11). Empty when it can be applied.\n */\n @Field(type => [String], { description: 'Definition errors that refuse this template when applied: unregistered tasks, or tasks naming undeclared privileges.' })\n definitionErrors: string[]\n\n @Field(type => Int, { description: 'How many privileges this template would actually grant here.' })\n privilegeCount: number\n\n @Field(type => Int, { description: 'How many privileges the template names that this installation does not have.' })\n missingCount: number\n\n /**\n * Whether a role already carries this template's default name in the current domain.\n *\n * The screen warns before creating one, because seeding onto an existing name does not\n * make a second role — approval lines hold a role's id, so a duplicate would leave the\n * approval routed to the role nobody edits. The existing role is used as it stands and\n * the template's privileges are added to it.\n *\n * It says \"default name\" rather than \"based on\" because it answers the question the\n * create dialog asks — will this name make a new role or top up a standing one. Which\n * template a role is based on is the role's own `templateId`.\n */\n @Field({ description: \"Whether a role already carries this template's default name in the current domain.\" })\n defaultNameTaken: boolean\n\n /**\n * True when the template deliberately opens nothing.\n *\n * A seat that only receives approvals is legitimate — operato-twin declares seven roles\n * with no privileges at all, used as the target an approval line points at. The screen\n * must not draw that as \"0 privileges, something is wrong\", so the distinction is\n * carried here rather than inferred from an empty list.\n */\n @Field({ description: 'Whether this template grants nothing on purpose, rather than by omission.' })\n grantsNothingDeliberately: boolean\n}\n\n@ObjectType({ description: 'Privileges on one category that a role lacks against its template.' })\nexport class RoleTemplateGapGrant {\n @Field({ description: 'The privilege category.' })\n category: string\n\n @Field(type => [String], { description: 'The axes the template grants here and the role does not hold.' })\n axes: string[]\n}\n\n@ObjectType({\n description: \"What the template of a role's name grants in this installation that the role does not hold.\"\n})\nexport class RoleTemplateGapView {\n @Field({ description: 'The template the role is paired with, by name.' })\n templateId: string\n\n @Field({ description: 'The template name - the same as the role name.' })\n templateName: string\n\n @Field(type => [RoleTemplateGapGrant], { description: 'The privileges missing from the role, by category.' })\n missing: RoleTemplateGapGrant[]\n\n @Field(type => Int, {\n description: 'How many privileges the role lacks against its template. Zero means up to date.'\n })\n missingCount: number\n\n /**\n * What the role holds beyond the template. Not a fault — a site may widen a role on purpose — so the screen\n * calls it \"wider than the template\" and nothing takes it away.\n */\n @Field(type => [RoleTemplateGapGrant], { description: 'Declared privileges the role holds that the template does not grant, by category.' })\n extra: RoleTemplateGapGrant[]\n\n @Field(type => Int, { description: 'How many privileges the role holds beyond the template.' })\n extraCount: number\n}\n\n@ObjectType({ description: \"One task laid against a role's privileges.\" })\nexport class RoleTaskCoverageView {\n @Field({ description: 'The task id, `<module>.<work>`. Its words are the translation keys task.<id>.name and task.<id>.description.' })\n taskId: string\n\n @Field({ description: 'The module or application that declares the task - the first part of its id.' })\n module: string\n\n @Field({\n description:\n \"all, some, none or broken. Says which of the task's privileges the role holds - not whether the work can be done. broken: the task names a privilege this installation does not declare.\"\n })\n status: string\n\n @Field(type => [RoleTemplateGapGrant], { description: \"The task's privileges the role holds.\" })\n held: RoleTemplateGapGrant[]\n\n @Field(type => [RoleTemplateGapGrant], { description: \"The task's declared privileges the role does not hold.\" })\n missing: RoleTemplateGapGrant[]\n\n @Field(type => [RoleTemplateGapGrant], { description: 'Privileges the task names that this installation does not declare.' })\n undeclared: RoleTemplateGapGrant[]\n}\n\n@ObjectType({ description: \"A role's privileges laid against every registered task.\" })\nexport class RoleTaskCoverageListView {\n @Field(type => [RoleTaskCoverageView], { description: 'Every registered task, with what the role holds of it.' })\n tasks: RoleTaskCoverageView[]\n\n @Field(type => [RoleTemplateGapGrant], { description: 'Declared privileges the role holds that no task names.' })\n unclassified: RoleTemplateGapGrant[]\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"role-template.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template.ts"],"names":[],"mappings":"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","sourcesContent":["import { logger } from '@things-factory/env'\n\nimport { readOnlyAxes, readOnlyAxesAreKnown } from '../privilege/privilege-axis.js'\nimport { mergeGrants, privilegeKey, roleTaskById, roleTasks, undeclaredTaskGrants } from './role-task.js'\n\n/**\n * Role templates — the seeds an application offers so a new domain does not start with\n * zero roles.\n *\n * ── Why this is a boot registry and not a table ───────────────────────────────\n * A template is a declaration. Copying it into rows per domain gives 100 domains x 3\n * templates = 300 rows, and once the code changes the template there is nowhere to ask\n * which of the two is true: the declaration, or the row that was written from an older\n * one. `@privilege` lives in the boot registry for the same reason.\n *\n * ── What a template is not ───────────────────────────────────────────────────\n * It is a seed, not a role. Once it has been used the resulting role belongs to the\n * administrator: the template is never re-applied and never overwrites. The role keeps only\n * which template it is based on (`Role.templateId`, one at most); what an administrator wants\n * from that (\"the template gained two privileges, here they are\") is the template's current\n * grants against the role's current privileges, and that is computed rather than stored.\n *\n * ── A role is one thing seen from three sides ────────────────────────────────\n * operato-plant uses roles as privilege bundles; operato-twin uses them as the seat an\n * approval line points at (`worklist` resolves an approver of type `Role` by id). Those\n * are not two concepts. A role is a seat in the organisation: what it opens, who fills it,\n * and whether approvals route to it. Splitting them would put two QUALITY-INSPECTORs in\n * one plant and force every person into both.\n */\n\n/** One module and the axes this template grants on it. Axes are always enumerated. */\nexport type RoleTemplateGrant = {\n /** `@privilege(category:)` — what is guarded, not the package name. */\n category: string\n /**\n * `query` / `mutation` / ... — listed one by one.\n *\n * There is deliberately no `'all'`. A template written as \"everything\" would widen on\n * its own the day a new axis is declared, with nobody having touched it. The single\n * place that may widen is the framework-computed administrator template, which means\n * \"whatever exists now\" by definition.\n */\n axes: string[]\n}\n\nexport type RoleTemplate = {\n /** Declaration key, namespaced by the declaring app: `plant.quality-inspector`. */\n id: string\n /**\n * The role name created in the domain by default: `QUALITY-INSPECTOR`.\n *\n * A role is paired with its template by `Role.templateId`, never by this name. Renaming\n * here still makes the next default-named role a second one beside the first, which\n * approval lines may already point at — renaming is a migration, not a template edit.\n */\n name: string\n /**\n * Plain text, not an i18n key.\n *\n * `createRoleFromTemplate` copies this onto the role it seeds, and the role list draws\n * the role's description as a value — so a key written here reaches the screen as\n * `label.role-template.administrator`. That is exactly what the framework's own two\n * templates did until 2026-09-11.\n *\n * operato-plant had it right from the start: \"검사 기준과 처분을 정하는 자리\".\n */\n description?: string\n /**\n * Required unless `tasks` is written, and may be empty.\n *\n * `[]` is a statement: this seat opens nothing, it exists for approval routing. A\n * missing `grants` is a different thing — someone forgot — and is rejected here, so the\n * screen can tell \"opens nothing on purpose\" from \"nothing was written down\".\n *\n * Read a template's privileges through `templateGrantsOf`, never from here: with `tasks`\n * this is only the part written directly.\n */\n grants?: RoleTemplateGrant[]\n /**\n * The work this seat does, by task id (`registerRoleTask`, ADR-0139 decision 11).\n *\n * The seat holds every privilege of every task here, plus `grants`, each privilege once.\n * A task id nobody registered is a definition error: boot reports it and the template is\n * not applied at all, rather than applied without that work.\n */\n tasks?: string[]\n}\n\n/**\n * Registered form of a template.\n *\n * Identical to the declaration today. It stays a separate name because the registry is\n * what every reader goes through, and giving it its own type keeps a later addition from\n * rippling into every call site.\n */\nexport type RegisteredRoleTemplate = RoleTemplate\n\nconst REGISTRY = 'ROLE_TEMPLATES'\n\nprocess[REGISTRY] = {}\n\nfunction registry(): Record<string, RegisteredRoleTemplate> {\n return process[REGISTRY]\n}\n\n/**\n * Declares a role template. Call it from the application's server entry.\n *\n * Categories are not checked here: registration runs while modules load, before the\n * schema is built and therefore before `process['PRIVILEGES']` exists. The name check\n * happens at boot instead (`reportUnknownTemplateGrants`).\n */\nexport function registerRoleTemplate(template: RoleTemplate): void {\n if (!template?.id || !template?.name) {\n throw new Error('A role template needs both an id and a name.')\n }\n\n if (template.tasks !== undefined && (!Array.isArray(template.tasks) || template.tasks.some(task => !task || typeof task !== 'string'))) {\n throw new Error(`Role template \"${template.id}\" has tasks that are not a list of task ids.`)\n }\n\n if (!Array.isArray(template.grants) && !template.tasks?.length) {\n throw new Error(\n `Role template \"${template.id}\" has no grants. Write \\`grants: []\\` if this seat is meant to ` +\n 'open nothing — leaving it out is indistinguishable from forgetting, and the screen cannot ' +\n 'tell an administrator which one it is.'\n )\n }\n\n for (const grant of template.grants || []) {\n if (!grant?.category || !Array.isArray(grant.axes) || grant.axes.length === 0) {\n throw new Error(\n `Role template \"${template.id}\" has a grant without a category or with no axes. ` +\n 'Axes are always listed one by one.'\n )\n }\n\n /*\n * Across the nine roles operato-plant declares, no role is granted `mutation` on a\n * category without `query` on the same category — you cannot fix what you cannot see.\n * A template that does so is almost certainly a slip, so say it out loud rather than\n * silently granting a half-usable seat.\n */\n if (grant.axes.includes('mutation') && !grant.axes.includes('query')) {\n logger.warn(\n `[role-template] \"${template.id}\" grants ${grant.category}:mutation without ${grant.category}:query. ` +\n 'Someone who may change a thing but not see it can rarely use either.'\n )\n }\n }\n\n if (registry()[template.id]) {\n throw new Error(`Role template \"${template.id}\" is declared twice.`)\n }\n\n registry()[template.id] = { ...template, grants: template.grants || [] }\n}\n\nexport function roleTemplates(): RegisteredRoleTemplate[] {\n return Object.values(registry())\n}\n\nexport function roleTemplateById(id: string): RegisteredRoleTemplate | undefined {\n return registry()[id]\n}\n\n/**\n * The two templates the framework can build without knowing a single category name.\n *\n * Everything else has to be declared by an application: a job title spans modules\n * (a production lead uses worklist *and* order *and* ops-master) and no one package knows\n * that. These two are different — they are defined over whatever is declared, so they can\n * be computed from the registry `@privilege` fills.\n */\nexport function computedRoleTemplates(): RegisteredRoleTemplate[] {\n const declared: [string, string][] = Object.values(process['PRIVILEGES'] || {})\n const axesOf = new Map<string, Set<string>>()\n\n for (const [category, axis] of declared) {\n if (!axesOf.has(category)) {\n axesOf.set(category, new Set())\n }\n axesOf.get(category)!.add(axis)\n }\n\n const categories = [...axesOf.keys()].sort()\n\n /*\n * No description on either of these.\n *\n * They carried i18n keys, which the seeding path stored verbatim onto the role and the\n * list then drew as the description. And a static sentence would be worse than what the\n * screen already computes from `grants` — \"opens 30 things across 30 modules\" is exact\n * and moves when the installation does.\n */\n /*\n * VIEWER is only offered when someone has said which axes are read-only.\n *\n * This used to match the literal word `query`, which is right in this house and wrong in\n * dssp, where the read axis is spelled `read` and `kpi` has no `query` at all — VIEWER\n * would have quietly dropped that installation's main module.\n *\n * And when nobody has said, the template is **not offered at all** rather than offered\n * empty. An empty VIEWER is the quiet failure this whole design is built to avoid: an\n * administrator picks it, gets a role, no door opens, and the list counts the zero\n * correctly. \"We do not know\" is not \"there are none\".\n */\n const readOnly = readOnlyAxes()\n\n const viewer: RoleTemplate | undefined = readOnlyAxesAreKnown()\n ? {\n id: 'framework.viewer',\n name: 'VIEWER',\n grants: categories\n .map(category => ({ category, axes: [...axesOf.get(category)!].filter(axis => readOnly.has(axis)).sort() }))\n .filter(grant => grant.axes.length > 0)\n }\n : undefined\n\n /*\n * The one place that widens on purpose.\n *\n * Everywhere else an unenumerated axis is a defect waiting for the day a new axis is\n * declared. Here \"everything that exists\" is the meaning of the template, so picking up\n * a newly declared axis is correct rather than accidental.\n */\n const administrator: RoleTemplate = {\n id: 'framework.administrator',\n name: 'ADMINISTRATOR',\n grants: categories.map(category => ({ category, axes: [...axesOf.get(category)!].sort() }))\n }\n\n return viewer ? [viewer, administrator] : [administrator]\n}\n\n/** Every template this installation offers — declared by apps, plus the computed two. */\nexport function allRoleTemplates(): RegisteredRoleTemplate[] {\n return [...computedRoleTemplates(), ...roleTemplates()]\n}\n\n/**\n * **A template's privileges** — its tasks' privileges and its own grants, each privilege once.\n *\n * Every reader of a template's privileges goes through this: the role screen's gap, applying it, the view the\n * template picker draws. A task id nobody registered adds nothing here; `templateDefinitionErrors` says so, and\n * applying the template refuses.\n */\nexport function templateGrantsOf(template: RoleTemplate): RoleTemplateGrant[] {\n const tasks = (template.tasks || []).map(id => roleTaskById(id)?.grants || [])\n\n return mergeGrants([...tasks, template.grants || []])\n}\n\n/**\n * What is wrong with a template as declared, before anything is applied (ADR-0139 decision 11).\n *\n * A task id nobody registered, and a privilege a task names that this installation does not declare. A task is\n * registered only by a module that is installed, so either one is a slip in code — a task id mistyped, a module the\n * application names but does not install, a category renamed under a task — and applying the template anyway would\n * give a seat that is short of work it says it does. So these refuse the template.\n *\n * A template's own `grants` naming an undeclared privilege is not here: an application may write grants for modules\n * one installation has and another does not, and those are skipped as before (`unknownTemplateGrants`).\n */\nexport function templateDefinitionErrors(template: RoleTemplate, declared: Record<string, unknown> = process['PRIVILEGES'] || {}): string[] {\n return (template.tasks || []).flatMap(id => {\n const task = roleTaskById(id)\n if (!task) return [`task \"${id}\" is not registered`]\n return undeclaredTaskGrants(task, declared).map(grant => `task \"${id}\" names ${grant}, which this installation does not declare`)\n })\n}\n\n/** What a role lacks against a template, and what it holds beyond it - see `templateGapAgainst`. */\nexport type RoleTemplateGap = {\n templateId: string\n templateName: string\n missing: { category: string; axes: string[] }[]\n missingCount: number\n /** Declared privileges the role holds that the template does not grant — wider than the template, not wrong. */\n extra: { category: string; axes: string[] }[]\n extraCount: number\n}\n\n/**\n * **A role laid against a template** (ADR-0075 decision 1, ADR-0139 decision 11).\n *\n * A template is a seed: the role it made belongs to the administrator afterwards, and nothing\n * pushes a template's later grants into it. When a template gains a door - operato-plant's nine\n * gained ai-assistant - the roles already standing fall quietly behind. The answer is the one this\n * file's header gives: the template's current grants against the role's current privileges,\n * computed, never stored. The screen shows it and the administrator adds it; nothing adds it for them.\n *\n * `extra` is the other side: what the role holds that the template does not grant. It is not a fault —\n * a site may have widened the role on purpose — and nothing takes it away; the screen says \"wider than\n * the template\". Only privileges this installation declares count on either side: a grant it does not\n * declare is `missingCount` on the template, and a held privilege nothing declares is struck through\n * on the role screen already.\n */\nexport function templateGapAgainst(\n template: RoleTemplate,\n held: ReadonlySet<string>,\n declared: Record<string, unknown> = process['PRIVILEGES'] || {}\n): RoleTemplateGap {\n const grants = templateGrantsOf(template)\n const granted = new Set(grants.flatMap(grant => grant.axes.map(axis => privilegeKey(grant.category, axis))))\n\n const missing = grants\n .map(grant => ({\n category: grant.category,\n axes: grant.axes.filter(axis => declared[privilegeKey(grant.category, axis)] && !held.has(privilegeKey(grant.category, axis)))\n }))\n .filter(grant => grant.axes.length > 0)\n\n const extra = mergeGrants([\n [...held]\n .filter(key => declared[key] && !granted.has(key))\n .sort()\n .map(key => {\n const [category, axis] = key.split(' ')\n return { category, axes: [axis] }\n })\n ])\n\n return {\n templateId: template.id,\n templateName: template.name,\n missing,\n missingCount: missing.reduce((count, grant) => count + grant.axes.length, 0),\n extra,\n extraCount: extra.reduce((count, grant) => count + grant.axes.length, 0)\n }\n}\n\n/**\n * The gap against the role's base template (`Role.templateId`) — what the role list shows unasked.\n *\n * Paired by the stored base, never by name (ADR-0075 decision 1, amended 2026-10-26): operato-office's roles are\n * named in the site's own words, and pairing by name left them without a gap to show. The role is never renamed\n * to make a pair: approval lines hold the role, and the name is the site's.\n * `null` when the role has no base, or its base is not declared in this installation; a gap of 0 missing is a\n * role that is up to date.\n */\nexport function templateGapOf(\n templateId: string | null | undefined,\n held: ReadonlySet<string>,\n templates: RegisteredRoleTemplate[] = allRoleTemplates(),\n declared: Record<string, unknown> = process['PRIVILEGES'] || {}\n): RoleTemplateGap | null {\n const template = templateId ? templates.find(one => one.id === templateId) : undefined\n return template ? templateGapAgainst(template, held, declared) : null\n}\n\n/**\n * Names a template's own grants mention that this installation does not declare.\n *\n * A template that survives a category rename becomes a role that grants nothing, and that\n * is the quietest way this design can fail — the administrator picks \"quality inspector\",\n * gets a role, and it opens no doors. operato-plant's seed already does this for its own\n * table (`seed-demo-org.ts` warns about grants it could not attach); this is the same\n * check moved to where templates are declared. A task's privileges are checked as\n * definition errors instead (`templateDefinitionErrors`).\n */\nexport function unknownTemplateGrants(template: RoleTemplate): string[] {\n const declared = process['PRIVILEGES'] || {}\n\n return (template.grants || []).flatMap(grant =>\n grant.axes.filter(axis => !declared[privilegeKey(grant.category, axis)]).map(axis => `${grant.category}:${axis}`)\n )\n}\n\n/**\n * Boot's one look at what templates and tasks name — after every module has loaded and `@privilege` has filled\n * `process['PRIVILEGES']`, the first moment both registries and the declarations are complete.\n */\nexport function reportUnknownTemplateGrants(): void {\n const unknown = roleTemplates().flatMap(template =>\n unknownTemplateGrants(template).map(grant => `${template.id} → ${grant}`)\n )\n\n if (unknown.length) {\n logger.warn(\n `[role-template] ${unknown.length} grants name privileges this installation does not declare — ` +\n `${unknown.join(', ')}. Privilege definitions come from @privilege at boot, so either the query ` +\n 'is not installed here or the name has moved. A role seeded from such a template opens nothing.'\n )\n }\n\n const broken = [\n ...roleTasks().flatMap(task => undeclaredTaskGrants(task).map(grant => `task \"${task.id}\" names ${grant}`)),\n ...roleTemplates().flatMap(template =>\n (template.tasks || []).filter(id => !roleTaskById(id)).map(id => `${template.id} → task \"${id}\" is not registered`)\n )\n ]\n\n if (broken.length) {\n logger.error(\n `[role-task] ${broken.length} definition errors — ${broken.join(', ')}. ` +\n 'A template that names any of these is refused when applied, so no seat is stood up short of the work it says it does.'\n )\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"domain-query.js","sourceRoot":"","sources":["../../../server/service/user/domain-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA4D;AAE5D,iDAA6D;AAC7D,uCAAgC;AAGzB,IAAM,WAAW,GAAjB,MAAM,WAAW;IAEhB,AAAN,KAAK,CAAC,SAAS,CAAS,MAAc;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAM;QACR,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,OAAO,CAAC;YAC9C,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,EAAE;SAC5B,CAAC,CAAA;QAEF,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACrE,MAAM,oEAAoE,CAAA;QAC5E,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;CACF,CAAA;AAjBY,kCAAW;AAEhB;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/B,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAS,cAAM;;4CAcrC;sBAhBU,WAAW;IADvB,IAAA,uBAAQ,EAAC,cAAM,CAAC;GACJ,WAAW,CAiBvB","sourcesContent":["import { FieldResolver, Resolver, Root } from 'type-graphql'\n\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { User } from './user.js'\n\n@Resolver(Domain)\nexport class DomainQuery {\n @FieldResolver(type => User, { nullable: true })\n async ownerUser(@Root() domain: Domain): Promise<User | null> {\n if (!domain.owner) {\n return\n }\n\n const owner = await getRepository(User).findOne({\n where: { id: domain.owner }\n })\n\n if (owner && owner.userType !== 'user' && owner.userType !== 'admin') {\n throw 'the type of the given user is not suitable for a owner of a domain'\n }\n\n return owner\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/user/index.ts"],"names":[],"mappings":";;;AAAA,uCAAgC;AAChC,mDAA2C;AAC3C,yDAAiD;AACjD,uDAA+C;AAElC,QAAA,QAAQ,GAAG,CAAC,cAAI,CAAC,CAAA;AACjB,QAAA,SAAS,GAAG,CAAC,yBAAS,EAAE,+BAAY,EAAE,6BAAW,CAAC,CAAA","sourcesContent":["import { User } from './user.js'\nimport { UserQuery } from './user-query.js'\nimport { UserMutation } from './user-mutation.js'\nimport { DomainQuery } from './domain-query.js'\n\nexport const entities = [User]\nexport const resolvers = [UserQuery, UserMutation, DomainQuery]\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"user-mutation.js","sourceRoot":"","sources":["../../../server/service/user/user-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAAqF;AACrF,qDAAqD;AACrD,qCAAsE;AAEtE,iDAAgG;AAEhG,qEAAmH;AACnH,yEAA6D;AAC7D,yEAA8E;AAC9E,yEAAsF;AACtF,+EAAyE;AACzE,2EAA0F;AAC1F,6CAAsC;AACtC,uCAA4C;AAC5C,mDAAuE;AACvE,iEAA+G;AAGxG,IAAM,YAAY,GAAlB,MAAM,YAAY;IAOjB,AAAN,KAAK,CAAC,UAAU,CACqE,IAAa,EACzF,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;QAChC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QAEzB,IAAI,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,uCAAuB,CAAC,CAAC,CAAA;QAChE,CAAC;QAED,IAAI,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,oCAAoB,CAAC,CAAC,CAAA;QAC7D,CAAC;QAED,6DAA6D;QAC7D,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,gCAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,+HAA+H;QAC/H,MAAM,OAAO,GAAkB,EAAE,CAAA;QACjC,IAAA,gCAAY,EAAC,OAAO,CAAC,CAAA;QAErB,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC;YAC/B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI;YAC3B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI;YAC3B,GAAG,IAAI;YACP,GAAG,OAAO;YACV,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,KAAK,EACH,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM;gBAC7B,CAAC,CAAC,MAAM,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;oBACnC,EAAE,EAAE,IAAA,YAAE,EAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACvC,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;iBAC1B,CAAC;gBACJ,CAAC,CAAC,EAAE;YACR,iBAAiB,EAAE,IAAI,IAAI,EAAE;SAC9B,CAAC,CAAA;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,UAAU,CAEd,KAAa,EACsE,KAAgB,EAC5F,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAuD,OAAO,CAAC,KAAK,CAAA;QACvG,MAAM,EAAE,GAA6B,IAAA,kDAA4B,EAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QACpF,MAAM,IAAI,GAAS,MAAM,EAAE;aACxB,QAAQ,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;aACpF,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC;aACxC,iBAAiB,CAAC,cAAc,EAAE,UAAU,CAAC;aAC7C,MAAM,EAAE,CAAA;QAEX,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,KAAK,CAAC,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;gBAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;aAC/D,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACjD,IAAI,CAAC,MAAM,GAAG,oBAAU,CAAC,SAAS,CAAA;QACpC,CAAC;QAED,OAAO,MAAM,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACxC,GAAG,IAAI;YACP,GAAG,KAAK;YACR,OAAO;SACD,CAAC,CAAA;IACX,CAAC;IAQK,AAAN,KAAK,CAAC,UAAU,CAC2D,QAAgB,EAClF,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAE5B,MAAM,IAAA,2BAAgB,EAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QAExC,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,WAAW,CAC0E,SAAmB,EACrG,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC5B,MAAM,IAAA,4BAAiB,EAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAA;QAE1C,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,UAAU,CACoE,QAAgB,EAC3F,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,OAAO,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC/C,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5D,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBACrC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,MAAM,eAAe,GAAa,OAAO,CAAC,OAAO,CAAA;QACjD,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAC3G,CAAC;QAED,OAAO,CAAC,OAAO,GAAG,CAAC,GAAG,eAAe,EAAE,MAAM,CAAC,CAAA;QAC9C,MAAM,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAElC,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,gBAAgB,CAC8E,QAAgB,EAC3G,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,IAAI,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC5C,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;SAChC,CAAC,CAAA;QAEF,IAAI,CAAC,IAAI,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,MAAM,eAAe,GAAW,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,UAAkB,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,CAAA;QAC3G,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAC9F,CAAC;QAED,mCAAmC;QACnC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAA;QAEvC,sCAAsC;QACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,CAAC,CAAA;QAE3E,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE/B,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,aAAa,CACgF,QAAgB,EAC1G,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACjD,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,IAAI,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC5C,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;SAChC,CAAC,CAAA;QAEF,IAAI,CAAC,IAAI,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,oBAAU,CAAC,SAAS,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;QACnF,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;QAC1D,CAAC;QAED,qDAAqD;QACrD,MAAM,sBAAsB,GAAa,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACrF,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC5F,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAA;QAC/G,CAAC;QAED,2DAA2D;QAC3D,MAAM,IAAA,2CAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QAEvD,OAAO,IAAI,CAAA;IACb,CAAC;IASK,AAAN,KAAK,CAAC,YAAY,CACoE,QAAgB,EAC7F,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,UAAU,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAClD,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBACxC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,UAAkB,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;QACjD,CAAC;QAED,2GAA2G;QAC3G,IAAI,UAAU,CAAC,MAAM,KAAK,oBAAU,CAAC,kBAAkB,EAAE,CAAC;YACxD,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,wEAAwE,CAAC,CAAA;QAC3H,CAAC;QAED,UAAU,CAAC,SAAS,GAAG,CAAC,CAAA;QACxB,UAAU,CAAC,MAAM,GAAG,oBAAU,CAAC,SAAS,CAAA;QAExC,MAAM,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAErC,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,cAAc,CACoE,QAAgB,EAC/F,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,UAAU,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAClD,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBACxC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,UAAkB,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;QACjD,CAAC;QAED,sDAAsD;QACtD,IAAI,UAAU,CAAC,QAAQ,IAAI,OAAO,IAAI,CAAC,MAAM,IAAA,mCAAa,EAAC,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACnD,CAAC;QAED,UAAU,CAAC,MAAM,GAAG,oBAAU,CAAC,QAAQ,CAAA;QAEvC,MAAM,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAErC,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;OAKG;IAQG,AAAN,KAAK,CAAC,sBAAsB,CAC8C,QAAgB,EACW,IAAa,EACzG,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,UAAU,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAClD,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBACxC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,UAAkB,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;QACjD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAA,6CAAoB,EAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;QAClE,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAA;QACf,CAAC;QAED,OAAO,MAAM,IAAA,0CAAsB,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;IAC9E,CAAC;IASK,AAAN,KAAK,CAAC,eAAe,CAC2E,QAAgB,EAE9G,cAA2B,EAE3B,aAA0B,EACnB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,IAAI,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC5C,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;SAChC,CAAC,CAAA;QAEF,IAAI,CAAC,IAAI,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAE;gBACjC,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;QAC1D,CAAC;QAED,qHAAqH;QACrH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,iCAAW,EACvC,IAAI,EACJ,EAAE,GAAG,EAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EACxF,MAAM,EACN,EAAE,EACF,OAAO,CAAC,KAAK,CAAC,IAAI,CACnB,CAAA;QAED,OAAO,KAAK,CAAA;IACd,CAAC;CACF,CAAA;AA9bY,oCAAY;AAOjB;IANL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,cAAI,EAAE;QACzB,WAAW,EACT,+HAA+H;KAClI,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC,CAAA;IACjF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CADmF,uBAAO;;8CA0CjG;AAQK;IANL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,cAAI,EAAE;QACzB,WAAW,EACT,+HAA+H;KAClI,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,qCAAmB,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC,CAAA;IAEtG,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC,CAAA;IACjF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDADoF,yBAAS;;8CA0BpG;AAQK;IANL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,mIAAmI;KACtI,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC,CAAA;IACvE,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8CAOP;AAQK;IANL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,uHAAuH;KAC1H,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC,CAAA;IACvF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+CAMP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,uIAAuI;KAC1I,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC,CAAA;IAChF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8CA8BP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,0IAA0I;KAC7I,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC,CAAA;IAChG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;oDAmCP;AAQK;IANL,IAAA,wBAAS,EAAC,8DAA8D,CAAC;IACzE,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,+HAA+H;KAClI,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC,CAAA;IAC/F,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAuCP;AASK;IAPL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,iHAAiH;KACpH,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC,CAAA;IAClF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDAoCP;AAQK;IANL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EACT,sHAAsH;KACzH,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC,CAAA;IACpF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kDAmCP;AAeK;IAPL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAiB,EAAE;QACtC,WAAW,EACT,qNAAqN;KACxN,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC,CAAA;IACtE,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC,CAAA;IACjG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DA+BP;AASK;IAPL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,cAAI,EAAE;QACzB,WAAW,EACT,2GAA2G;KAC9G,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC,CAAA;IAC5F,mBAAA,IAAA,kBAAG,EAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC,CAAA;IAElH,mBAAA,IAAA,kBAAG,EAAC,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC,CAAA;IAE3G,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAmCP;uBA7bU,YAAY;IADxB,IAAA,uBAAQ,EAAC,cAAI,CAAC;GACF,YAAY,CA8bxB","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, In, SelectQueryBuilder, EntityManager } from 'typeorm'\n\nimport { Domain, getRepository, ObjectRef, Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { deleteUser as commonDeleteUser, deleteUsers as commonDeleteUsers } from '../../controllers/delete-user.js'\nimport { assignRoles } from '../../utils/assignable-roles.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { isDomainOwner, setPrimaryDomainOwner } from '../../utils/domain-ownership.js'\nimport { passwordResetRefusal } from '../../utils/password-reset-rule.js'\nimport { issuePasswordSetupLink, sealPassword } from '../../controllers/password-setup.js'\nimport { Role } from '../role/role.js'\nimport { User, UserStatus } from './user.js'\nimport { NewUser, PasswordSetupLink, UserPatch } from './user-types.js'\nimport { USERNAME_ALREADY_EXISTS, EMAIL_ALREADY_EXISTS, USERNAME_INVALID } from '../../constants/error-code.js'\n\n@Resolver(User)\nexport class UserMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Creates a new user in the system. Use this mutation to register a user with the specified credentials and domain information.'\n })\n async createUser(\n @Arg('user', { description: 'The input object containing new user information.' }) user: NewUser,\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const { username, email } = user\n const userRepository = getRepository(User, tx)\n\n user.username = username.trim()\n user.email = email.trim()\n\n if (await userRepository.findOne({ where: { username: user.username } })) {\n throw new Error(context.t('error.' + USERNAME_ALREADY_EXISTS))\n }\n\n if (await userRepository.findOne({ where: { email: ILike(user.email) } })) {\n throw new Error(context.t('error.' + EMAIL_ALREADY_EXISTS))\n }\n\n // TODO username은 다음 패턴을 따라야 한다. pattern=\"^[A-Za-z0-9._@-]+$\"\n if (!/^[A-Za-z0-9._@-]+$/.test(user.username)) {\n throw new Error(context.t('error.' + USERNAME_INVALID, { username: user.username }))\n }\n\n /* Nobody sets the new person's password - it waits for them, and `issuePasswordSetupLink` hands them the way in (ADR-0161). */\n const account: Partial<User> = {}\n sealPassword(account)\n\n return await userRepository.save({\n creator: context.state.user,\n updater: context.state.user,\n ...user,\n ...account,\n domains: [domain],\n roles:\n user.roles && user.roles.length\n ? await getRepository(Role, tx).findBy({\n id: In(user.roles.map(role => role.id)),\n domain: { id: domain.id }\n })\n : [],\n passwordUpdatedAt: new Date()\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Updates the information of an existing user. Use this mutation to modify user profile details, such as name, email, or roles.'\n })\n async updateUser(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to update.' })\n email: string,\n @Arg('patch', { description: 'The patch object containing updated user fields.' }) patch: UserPatch,\n @Ctx() context: ResolverContext\n ) {\n const { domain, user: updater, tx }: { domain: Domain; user: User; tx?: EntityManager } = context.state\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n const user: User = await qb\n .andWhere('LOWER(USER.email) = :email', { email: email?.toLowerCase().trim() || '' })\n .leftJoinAndSelect('USER.roles', 'ROLES')\n .leftJoinAndSelect('ROLES.domain', 'R_DOMAIN')\n .getOne()\n\n if (patch.roles) {\n patch.roles = await getRepository(Role, tx).find({\n where: { id: In(patch.roles.map((r: Partial<Role>) => r.id)) }\n })\n }\n\n if (patch.status && patch.status === 'activated') {\n user.status = UserStatus.ACTIVATED\n }\n\n return await getRepository(User, tx).save({\n ...user,\n ...patch,\n updater\n } as any)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes a user from the system by their username. Use this mutation to permanently remove a user account and revoke their access.'\n })\n async deleteUser(\n @Arg('username', { description: 'The username of the user to delete.' }) username: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n\n await commonDeleteUser({ username }, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes multiple users from the system by their usernames. Use this mutation to remove several user accounts at once.'\n })\n async deleteUsers(\n @Arg('usernames', type => [String], { description: 'An array of usernames to delete.' }) usernames: string[],\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n await commonDeleteUsers({ usernames }, tx)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, {\n description:\n 'Invites an existing user to join the current domain. Use this mutation to add a user to a domain if they already exist in the system.'\n })\n async inviteUser(\n @Arg('username', { description: 'The username or email of the user to invite.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var invitee: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!invitee && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n invitee = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!invitee) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const existingDomains: Domain[] = invitee.domains\n if (existingDomains.find((d: Domain) => d.id === domain.id)) {\n throw new Error(context.t('error.x already exists in y', { x: context.t('field.user'), y: domain.name }))\n }\n\n invitee.domains = [...existingDomains, domain]\n await userRepository.save(invitee)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, {\n description:\n \"Removes a user from the current domain. Use this mutation to revoke a user's access to a specific domain without deleting their account.\"\n })\n async deleteDomainUser(\n @Arg('username', { description: 'The username or email of the user to remove from the domain.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const targetDomainIdx: number = user.domains.findIndex((userDomain: Domain) => userDomain.id === domain.id)\n if (targetDomainIdx < 0) {\n throw new Error(context.t('error.x is not a member of y', { x: user.name, y: domain.name }))\n }\n\n // Remove domain relation with user\n user.domains.splice(targetDomainIdx, 1)\n\n // Remove domain's roles that user has\n user.roles = user.roles.filter((role: Role) => role.domainId !== domain.id)\n\n await userRepository.save(user)\n\n return true\n }\n\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Transfers domain ownership to another user. Use this mutation to assign the owner role to a different user within the domain.'\n })\n async transferOwner(\n @Arg('username', { description: 'The username or email of the user to transfer ownership to.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx, user: actor } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.status !== UserStatus.ACTIVATED) {\n throw new Error('Only activated users are eligible to receive admin privileges.')\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n /* 역할은 부모 도메인으로부터 상속될 수 있으므로 부여된 상속 역할도 자격으로 인정한다. */\n const grantableRoleDomainIds: string[] = [domain.id, domain.parentId].filter(Boolean)\n if (user.roles.filter((r: Role) => grantableRoleDomainIds.includes(r.domainId)).length == 0) {\n throw new Error(`Only users with at least one role in this domain are eligible to receive admin privileges.`)\n }\n\n /* 대표 오너 컬럼과 `DomainOwner` 테이블을 함께 갱신한다 (기존 오너 엔트리는 유지). */\n await setPrimaryDomainOwner(domain, user.id, actor, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @UseMiddleware(SpeakRefusals)\n @Mutation(returns => Boolean, {\n description:\n 'Activates a user account in the current domain. Use this mutation to enable a user who was previously inactive.'\n })\n async activateUser(\n @Arg('username', { description: 'The username or email of the user to activate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n /* Activating does not give a password - a waiting account opens only through its setup link (ADR-0161). */\n if (targetUser.status === UserStatus.PWD_RESET_REQUIRED) {\n throw new Refusal('password-setup-pending', {}, 'This account is waiting for its password. Issue a password setup link.')\n }\n\n targetUser.failCount = 0\n targetUser.status = UserStatus.ACTIVATED\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Inactivates a user account in the current domain. Use this mutation to disable a user, making them unable to log in.'\n })\n async inactivateUser(\n @Arg('username', { description: 'The username or email of the user to inactivate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n /* 공동 오너(`DomainOwner` 테이블)도 도메인 관리자이므로 같은 보호를 받는다. */\n if (targetUser.userType == 'admin' || (await isDomainOwner(domain, targetUser, tx))) {\n throw new Error('Admin deactivation not allowed')\n }\n\n targetUser.status = UserStatus.INACTIVE\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n /**\n * The person sets their own password through a one-time link - an administrator never sets it (ADR-0161).\n *\n * For an account this domain made and one whose password it takes back alike. Who may be handed one from here is\n * `password-reset-rule.ts`: someone wholly of this domain, owning no domain.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @UseMiddleware(SpeakRefusals)\n @Mutation(returns => PasswordSetupLink, {\n description:\n \"Puts the user waiting for a password they set themselves and issues the one-time link to it. The current password stops working and open sessions end. Mailed, or returned once for the administrator to hand over.\"\n })\n async issuePasswordSetupLink(\n @Arg('username', { description: 'The username or email of the user.' }) username: string,\n @Arg('mail', { description: \"True mails the link to the user's address; false returns it once.\" }) mail: boolean,\n @Ctx() context: ResolverContext\n ): Promise<PasswordSetupLink> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n const refusal = await passwordResetRefusal(targetUser, domain, tx)\n if (refusal) {\n throw refusal\n }\n\n return await issuePasswordSetupLink({ user: targetUser, mail, context, tx })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @UseMiddleware(SpeakRefusals)\n @Mutation(returns => User, {\n description:\n \"Updates the roles assigned to a user. Use this mutation to change a user's permissions within the domain.\"\n })\n async updateUserRoles(\n @Arg('username', { description: 'The username or email of the user whose roles to update.' }) username: string,\n @Arg('availableRoles', type => [ObjectRef], { description: 'The list of roles that can be assigned to the user.' })\n availableRoles: ObjectRef[],\n @Arg('selectedRoles', type => [ObjectRef], { description: 'The list of roles to be assigned to the user.' })\n selectedRoles: ObjectRef[],\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n /* Found again in this domain's scope - the ids are the client's (utils/assignable-roles.ts, ADR-0103 decision 2). */\n const { user: saved } = await assignRoles(\n user,\n { add: selectedRoles.map(role => role.id), remove: availableRoles.map(role => role.id) },\n domain,\n tx,\n context.state.user\n )\n\n return saved\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"user-query.js","sourceRoot":"","sources":["../../../server/service/user/user-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, SelectQueryBuilder } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, ListParam, getQueryBuilderFromListParams } from '@things-factory/shell'\n\nimport { whereAssignableBy } from '../../utils/assignable-roles.js'\nimport { checkUserBelongsDomain } from '../../utils/check-user-belongs-domain.js'\nimport { findDomainOwnerUserIds } from '../../utils/domain-ownership.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { passwordResetRefusal } from '../../utils/password-reset-rule.js'\nimport { Role } from '../role/role.js'\nimport { User } from './user.js'\nimport { PasswordRule, UserList } from './user-types.js'\n\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\n@Resolver(User)\nexport class UserQuery {\n @Query(returns => PasswordRule, {\n description:\n 'Retrieves the current password rule configuration for the system, such as required character types and minimum length.'\n })\n passwordRule(@Ctx() context: ResolverContext): PasswordRule {\n return passwordRule\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => User, {\n description: 'Fetches a user by their email address within the current domain.'\n })\n async user(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to fetch.' })\n email: string,\n @Ctx() context: ResolverContext\n ): Promise<User> {\n const { domain } = context.state\n\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n qb.andWhere(`LOWER(USER.email) = :email`, { email: email.toLowerCase().trim() })\n\n return qb.getOne()\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => UserList, {\n description: 'Fetches a list of users based on provided search parameters within the current domain.'\n })\n async users(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<UserList> {\n const { domain } = context.state\n\n const qb = getQueryBuilderFromListParams({\n repository: getRepository(User),\n params,\n alias: 'USER',\n searchables: ['name', 'email', 'description']\n })\n\n qb.select().andWhere(qb => {\n const subQuery = qb\n .subQuery()\n .select('USERS_DOMAINS.users_id')\n .from('users_domains', 'USERS_DOMAINS')\n .where('USERS_DOMAINS.domains_id = :domainId', { domainId: domain.id })\n .getQuery()\n\n return 'USER.id IN ' + subQuery\n })\n\n const [items, total] = await qb.getManyAndCount()\n\n /* 오너는 여러 명일 수 있다 — 대표 오너 컬럼만 보면 공동 오너가 일반 사용자로 표시된다. */\n const ownerIds: Set<string> = await findDomainOwnerUserIds(domain)\n\n const foundUsers: User[] = items.map((item: User) => {\n item.owner = ownerIds.has(item.id)\n return item\n })\n\n return { items: foundUsers, total }\n }\n\n @Query(returns => Boolean, {\n description: 'Checks if the current authenticated user belongs to the current domain.'\n })\n async checkUserBelongsDomain(@Ctx() context: ResolverContext): Promise<Boolean> {\n const { user, domain } = context.state\n\n if (user) {\n return await checkUserBelongsDomain(domain, user)\n } else {\n throw new Error(`Failed to get current user information.`)\n }\n }\n\n /** The user screen offers a password setup link only where `issuePasswordSetupLink` would not refuse (`password-reset-rule.ts`). */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, {\n description:\n \"Whether this domain may issue the user a password setup link: the user works in no other domain, owns no domain and is not an administrator.\"\n })\n async mayIssuePasswordSetupLink(\n @Arg('id', { description: 'The id of the user who would be issued the link.' }) id: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain } = context.state\n\n const target: User = await getRepository(User).findOne({ where: { id }, relations: ['domains'] })\n if (!target?.domains?.find(userDomain => userDomain.id === domain.id)) {\n return false\n }\n\n return !(await passwordResetRefusal(target, domain))\n }\n\n /** Asked only by the add-user popup, before inviteUser / createUser - so it is gated as they are. */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, {\n description: 'Checks if a user with the given email address exists in the system.'\n })\n async checkUserExistence(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address to check for existence.' })\n email: string\n ): Promise<Boolean> {\n return Boolean(await getRepository(User).count({ where: { email: ILike(email) } }))\n }\n\n @FieldResolver({ description: 'Returns the username of the user, or their email if username is not set.' })\n async username(@Root() user: User) {\n return user.username || user.email\n }\n\n @FieldResolver({ description: 'Returns the list of domains the user belongs to.' })\n async domains(@Root() user: User) {\n return (\n await getRepository(User).findOne({\n where: { id: user.id },\n relations: ['domains']\n })\n ).domains\n }\n\n /**\n * 이 도메인에서 본 역할 — 이 도메인이 부여할 수 있는 범위 안의 것만.\n *\n * 범위는 셋이다: 내 도메인의 역할, 부모 1단의 역할(`userRoles` · `myRoles` 와 같다),\n * 파트너가 이 도메인에 grant 한 역할(`grantedRoles`). 사용자 상세(`user-detail`)가 부여할\n * 수 있다고 내놓는 목록이 정확히 이 셋이고, 체크 상태를 이 필드로 채운다. 셋째를 빼면\n * 파트너 역할이 체크 안 된 채 그려지고, 저장하면 **떼어진다**.\n *\n * 예전에는 사용자가 가진 역할을 **전부** 돌려줬다. 도메인 A 와 B 에 함께 속한 사람을\n * A 의 사용자 목록으로 읽으면 B 의 역할 이름이 보였다(`role-query.ts` 의 ③ 과 같은 결함).\n */\n @FieldResolver({\n description: 'Returns the roles the user holds that the current domain can assign: its own, its parent domain\\'s, and roles granted to it by partners.'\n })\n async roles(@Root() user: User, @Ctx() context: ResolverContext): Promise<Role[]> {\n const { domain } = context.state\n\n const qb = getRepository(Role)\n .createQueryBuilder('ROLE')\n .innerJoin('ROLE.users', 'USER', 'USER.id = :userId', { userId: user.id })\n\n /* The same scope the roles are assigned in (utils/assignable-roles.ts) - what is shown is what can be saved. */\n return await whereAssignableBy(qb, 'ROLE', domain).getMany()\n }\n\n @FieldResolver({ description: 'Returns the user who last updated this user record.' })\n async updater(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.updaterId })\n }\n\n @FieldResolver({ description: 'Returns the user who originally created this user record.' })\n async creator(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.creatorId })\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"user-types.js","sourceRoot":"","sources":["../../../server/service/user/user-types.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectType, InputType, Field, ID, Int } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ObjectRef } from '@things-factory/shell'\nimport { User } from './user.js'\n\n// This file defines GraphQL types for user management, including password rules, user creation, user patching, and user list results.\n\n@ObjectType({\n description: 'Password rule configuration for user accounts, specifying required character types and patterns.'\n})\nexport class PasswordRule {\n @Field({ nullable: true, description: 'Whether at least one lowercase letter is required.' })\n lowerCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one uppercase letter is required.' })\n upperCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one digit is required.' })\n digit?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one special character is required.' })\n specialCharacter?: boolean\n\n @Field({ nullable: true, description: 'Whether repeated characters are allowed in the password.' })\n allowRepeat?: boolean\n\n @Field({ nullable: true, description: 'Whether to enforce a strict password pattern.' })\n useTightPattern?: boolean\n\n @Field({ nullable: true, description: 'Whether to allow a loose password pattern.' })\n useLoosePattern?: boolean\n\n @Field({ nullable: true, description: 'Minimum length required for a strict password pattern.' })\n tightCharacterLength?: number\n\n @Field({ nullable: true, description: 'Minimum length required for a loose password pattern.' })\n looseCharacterLength?: number\n}\n\n@InputType({ description: 'Input type for creating a new user account.' })\nexport class NewUser {\n @Field({ description: 'Unique username for the user.' })\n username: string\n\n @Field({ description: 'Full name of the user.' })\n name: string\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { description: 'Email address of the user.' })\n email: string\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n}\n\n@InputType({ description: 'Input type for updating (patching) an existing user account.' })\nexport class UserPatch {\n @Field(type => ID, { nullable: true, description: 'Unique identifier of the user to update.' })\n id?: string\n\n @Field({ nullable: true, description: 'Full name of the user.' })\n name?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Domains associated with the user.' })\n domains?: [ObjectRef]\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { nullable: true, description: 'Email address of the user.' })\n email?: string\n\n @Field({ nullable: true, description: 'Status of the user account (e.g., active, inactive).' })\n status?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field({ nullable: true, description: 'Change flag for tracking updates (e.g., C/U/D for create/update/delete).' })\n cuFlag?: string\n}\n\n/** What issuing a password setup link answers (ADR-0161). The link is here only when it was not mailed - and only now. */\n@ObjectType({ description: 'The password setup link just issued: mailed, or returned once to be handed over.' })\nexport class PasswordSetupLink {\n @Field({ nullable: true, description: 'The link, when it was not mailed. It is not kept and cannot be read again.' })\n link?: string\n\n @Field({ nullable: true, description: 'The address the link was mailed to, when it was mailed.' })\n mailedTo?: string\n\n @Field({ description: 'When the link stops working.' })\n expiresAt: Date\n}\n\n@ObjectType({ description: 'A paginated list of users and the total count.' })\nexport class UserList {\n @Field(type => [User], { nullable: true, description: 'List of user objects.' })\n items: User[]\n\n @Field(type => Int, { nullable: true, description: 'Total number of users matching the query.' })\n total: number\n}\n"]}