@flowdular/sdk 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (912) hide show
  1. package/.ai/agents/sandbox/business-manager.md +5 -1
  2. package/.ai/blueprints/author-spec/README.md +1 -1
  3. package/.ai/blueprints/author-spec/spec-requirements.yaml +44 -0
  4. package/.ai/blueprints/author-spec/steps.yaml +5 -5
  5. package/.ai/blueprints/author-spec/templates/module.yaml +99 -3
  6. package/.ai/blueprints/edit-module/gates.yaml +4 -0
  7. package/.ai/blueprints/edit-module/required-files.yaml +9 -0
  8. package/.ai/blueprints/new-module/gates.yaml +4 -0
  9. package/.ai/blueprints/new-module/spec-requirements.yaml +2 -2
  10. package/.ai/blueprints/release/gates.yaml +4 -0
  11. package/.ai/platform-capabilities.md +128 -0
  12. package/.ai/policies/capabilities.yaml +100 -0
  13. package/.ai/policies/path-ownership.yaml +5 -2
  14. package/.ai/policies/task-budgets.yaml +5 -3
  15. package/.ai/references/catalog/module.json +4 -4
  16. package/.ai/references/catalog/package.json +2 -2
  17. package/.ai/references/catalog/spec/module.yaml +5 -3
  18. package/.ai/references/catalog/src/platform.ts +2 -0
  19. package/.ai/references/catalog/src/services/catalog-service.ts +89 -1
  20. package/.ai/references/catalog/src/services/data-classes.ts +47 -0
  21. package/.ai/references/catalog/src/services/database-repository.ts +98 -1
  22. package/.ai/references/catalog/src/services/repository.ts +22 -1
  23. package/.ai/references/catalog/tests/data-classes.test.ts +157 -0
  24. package/.ai/references/catalog.provenance.json +12 -10
  25. package/.ai/skills/README.md +10 -0
  26. package/.ai/skills/agent-tool-design/SKILL.md +1 -2
  27. package/.ai/skills/auth-security-review/SKILL.md +1 -1
  28. package/.ai/skills/business-agent-design/SKILL.md +0 -1
  29. package/.ai/skills/deploy-operate/SKILL.md +114 -0
  30. package/.ai/skills/module-new/SKILL.md +29 -3
  31. package/.ai/skills/module-update/SKILL.md +9 -3
  32. package/.ai/skills/perf-audit/SKILL.md +0 -1
  33. package/.ai/skills/release-eject-pr/SKILL.md +0 -1
  34. package/.ai/skills/spec-interview/SKILL.md +120 -0
  35. package/.ai/skills/test-hardening/SKILL.md +1 -0
  36. package/.ai/skills/ux-design/SKILL.md +34 -3
  37. package/.ai/skills/variables/SKILL.md +0 -2
  38. package/.ai/skills/workflow-development/SKILL.md +0 -1
  39. package/AGENTS.md +4 -0
  40. package/docs/agent-contract.md +2 -2
  41. package/docs/design-system.md +185 -31
  42. package/modules/access/migrations/0001_access_core.down.sql +1 -0
  43. package/modules/access/migrations/0001_access_core.up.sql +28 -0
  44. package/modules/access/migrations/README.md +3 -0
  45. package/modules/access/module.json +33 -0
  46. package/modules/access/package.json +43 -0
  47. package/modules/access/spec/module.yaml +225 -0
  48. package/modules/access/src/acl/permissions.ts +6 -0
  49. package/modules/access/src/api/endpoints.ts +244 -0
  50. package/modules/access/src/client/ActivityView.tsrx +245 -0
  51. package/modules/access/src/client/AttestationsView.tsrx +176 -0
  52. package/modules/access/src/client/ReviewView.tsrx +586 -0
  53. package/modules/access/src/client/api.ts +132 -0
  54. package/modules/access/src/client/contribution.tsrx +41 -0
  55. package/modules/access/src/client/index.ts +41 -0
  56. package/modules/access/src/client/navigation.ts +57 -0
  57. package/modules/access/src/client/presentation.ts +69 -0
  58. package/modules/access/src/client/state.ts +72 -0
  59. package/modules/access/src/domain/types.ts +189 -0
  60. package/modules/access/src/index.ts +38 -0
  61. package/modules/access/src/platform.ts +60 -0
  62. package/modules/access/src/server/index.ts +42 -0
  63. package/modules/access/src/server/runtime.ts +105 -0
  64. package/modules/access/src/services/access-service.ts +298 -0
  65. package/modules/access/src/services/audit-window.ts +78 -0
  66. package/modules/access/src/services/auth-directory.ts +72 -0
  67. package/modules/access/src/services/changes.ts +91 -0
  68. package/modules/access/src/services/data-classes.ts +28 -0
  69. package/modules/access/src/services/database-repository.ts +185 -0
  70. package/modules/access/src/services/directory.ts +102 -0
  71. package/modules/access/src/services/index.ts +33 -0
  72. package/modules/access/src/services/list-exports.ts +150 -0
  73. package/modules/access/src/services/migration.ts +54 -0
  74. package/modules/access/src/services/repository.ts +30 -0
  75. package/modules/access/src/services/review.ts +178 -0
  76. package/modules/access/translations/en.json +155 -0
  77. package/modules/access/translations/pl.json +155 -0
  78. package/modules/agents/migrations/0022_credential_rotation_inventory.down.sql +2 -0
  79. package/modules/agents/migrations/0022_credential_rotation_inventory.up.sql +7 -0
  80. package/modules/agents/migrations/0023_agents_retention_indexes.down.sql +2 -0
  81. package/modules/agents/migrations/0023_agents_retention_indexes.up.sql +9 -0
  82. package/modules/agents/migrations/0024_agent_meter_refusals.down.sql +3 -0
  83. package/modules/agents/migrations/0024_agent_meter_refusals.up.sql +22 -0
  84. package/modules/agents/migrations/0025_agent_list_indexes.down.sql +3 -0
  85. package/modules/agents/migrations/0025_agent_list_indexes.up.sql +11 -0
  86. package/modules/agents/module.json +30 -4
  87. package/modules/agents/package.json +3 -2
  88. package/modules/agents/spec/module.yaml +63 -5
  89. package/modules/agents/src/api/endpoints.ts +291 -33
  90. package/modules/agents/src/cli/commands.json +11 -0
  91. package/modules/agents/src/cli/index.ts +45 -0
  92. package/modules/agents/src/client/AgentDashboardWidget.tsrx +19 -11
  93. package/modules/agents/src/client/AgentDefinitionList.tsrx +53 -10
  94. package/modules/agents/src/client/AgentPlaygroundView.tsrx +4 -4
  95. package/modules/agents/src/client/AgentProceduresView.tsrx +2 -2
  96. package/modules/agents/src/client/AgentRunsView.tsrx +143 -40
  97. package/modules/agents/src/client/AgentsView.tsrx +109 -33
  98. package/modules/agents/src/client/api.ts +99 -21
  99. package/modules/agents/src/client/state.ts +97 -0
  100. package/modules/agents/src/domain/types.ts +44 -4
  101. package/modules/agents/src/platform.ts +42 -0
  102. package/modules/agents/src/server/action-execution.ts +201 -106
  103. package/modules/agents/src/server/runtime.ts +32 -0
  104. package/modules/agents/src/services/agent-service.ts +158 -30
  105. package/modules/agents/src/services/credential-rotation.ts +185 -0
  106. package/modules/agents/src/services/credential-vault.ts +71 -46
  107. package/modules/agents/src/services/data-classes.ts +176 -0
  108. package/modules/agents/src/services/database-repository.ts +393 -101
  109. package/modules/agents/src/services/metering.ts +111 -0
  110. package/modules/agents/src/services/migration.ts +109 -0
  111. package/modules/agents/src/services/module-agent-preflight.ts +17 -9
  112. package/modules/agents/src/services/notifications.ts +98 -0
  113. package/modules/agents/src/services/provider-repository.ts +9 -18
  114. package/modules/agents/src/services/provider-service.ts +1 -9
  115. package/modules/agents/src/services/reports.ts +107 -0
  116. package/modules/agents/src/services/repository.ts +86 -6
  117. package/modules/agents/src/services/usage-service.ts +2 -0
  118. package/modules/agents/src/services/worker.ts +114 -4
  119. package/modules/agents/src/settings.ts +14 -3
  120. package/modules/agents/translations/en.json +14 -4
  121. package/modules/agents/translations/pl.json +14 -4
  122. package/modules/approvals/migrations/0001_approvals_core.down.sql +3 -0
  123. package/modules/approvals/migrations/0001_approvals_core.up.sql +78 -0
  124. package/modules/approvals/migrations/0002_approvals_expiry_routing_role.down.sql +2 -0
  125. package/modules/approvals/migrations/0002_approvals_expiry_routing_role.up.sql +19 -0
  126. package/modules/approvals/migrations/0003_approvals_retention_indexes.down.sql +6 -0
  127. package/modules/approvals/migrations/0003_approvals_retention_indexes.up.sql +14 -0
  128. package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.down.sql +5 -0
  129. package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.up.sql +12 -0
  130. package/modules/approvals/migrations/README.md +3 -0
  131. package/modules/approvals/module.json +34 -0
  132. package/modules/approvals/package.json +43 -0
  133. package/modules/approvals/spec/module.yaml +318 -0
  134. package/modules/approvals/src/acl/permissions.ts +7 -0
  135. package/modules/approvals/src/api/endpoints.ts +457 -0
  136. package/modules/approvals/src/client/ApprovalRequestDrawer.tsrx +138 -0
  137. package/modules/approvals/src/client/ApprovalsView.tsrx +562 -0
  138. package/modules/approvals/src/client/PendingCountWidget.tsrx +52 -0
  139. package/modules/approvals/src/client/api.ts +203 -0
  140. package/modules/approvals/src/client/approvals.css +51 -0
  141. package/modules/approvals/src/client/contribution.tsrx +44 -0
  142. package/modules/approvals/src/client/index.ts +26 -0
  143. package/modules/approvals/src/client/navigation.ts +39 -0
  144. package/modules/approvals/src/client/pending-count.ts +26 -0
  145. package/modules/approvals/src/client/presentation.ts +52 -0
  146. package/modules/approvals/src/client/state.ts +141 -0
  147. package/modules/approvals/src/domain/capability.ts +95 -0
  148. package/modules/approvals/src/domain/types.ts +154 -0
  149. package/modules/approvals/src/index.ts +52 -0
  150. package/modules/approvals/src/platform.ts +128 -0
  151. package/modules/approvals/src/server/index.ts +42 -0
  152. package/modules/approvals/src/server/runtime.ts +181 -0
  153. package/modules/approvals/src/services/approvals-service.ts +557 -0
  154. package/modules/approvals/src/services/callbacks.ts +75 -0
  155. package/modules/approvals/src/services/data-classes.ts +167 -0
  156. package/modules/approvals/src/services/database-repository.ts +724 -0
  157. package/modules/approvals/src/services/eligibility.ts +121 -0
  158. package/modules/approvals/src/services/expiry-runner.ts +81 -0
  159. package/modules/approvals/src/services/index.ts +31 -0
  160. package/modules/approvals/src/services/migration.ts +222 -0
  161. package/modules/approvals/src/services/notifications.ts +95 -0
  162. package/modules/approvals/src/services/repository.ts +161 -0
  163. package/modules/approvals/src/services/service-error.ts +70 -0
  164. package/modules/approvals/src/settings.ts +102 -0
  165. package/modules/approvals/translations/en.json +104 -0
  166. package/modules/approvals/translations/pl.json +104 -0
  167. package/modules/audit/migrations/0001_audit_core.down.sql +8 -0
  168. package/modules/audit/migrations/0001_audit_core.up.sql +107 -0
  169. package/modules/audit/migrations/0002_audit_sweep_routing_role.down.sql +2 -0
  170. package/modules/audit/migrations/0002_audit_sweep_routing_role.up.sql +21 -0
  171. package/modules/audit/migrations/0003_audit_export_request.down.sql +12 -0
  172. package/modules/audit/migrations/0003_audit_export_request.up.sql +40 -0
  173. package/modules/audit/migrations/0004_audit_hold_seal_erasure.down.sql +17 -0
  174. package/modules/audit/migrations/0004_audit_hold_seal_erasure.up.sql +192 -0
  175. package/modules/audit/migrations/0005_audit_event_format.down.sql +17 -0
  176. package/modules/audit/migrations/0005_audit_event_format.up.sql +60 -0
  177. package/modules/audit/migrations/0006_audit_own_class_walks.down.sql +3 -0
  178. package/modules/audit/migrations/0006_audit_own_class_walks.up.sql +14 -0
  179. package/modules/audit/migrations/README.md +3 -0
  180. package/modules/audit/module.json +32 -0
  181. package/modules/audit/package.json +49 -0
  182. package/modules/audit/spec/module.yaml +809 -0
  183. package/modules/audit/src/acl/permissions.ts +7 -0
  184. package/modules/audit/src/api/endpoints.ts +315 -0
  185. package/modules/audit/src/cli/commands.json +95 -0
  186. package/modules/audit/src/cli/index.ts +658 -0
  187. package/modules/audit/src/client/DataClassesView.tsrx +420 -0
  188. package/modules/audit/src/client/ExportsView.tsrx +256 -0
  189. package/modules/audit/src/client/HoldsView.tsrx +604 -0
  190. package/modules/audit/src/client/SweepsView.tsrx +251 -0
  191. package/modules/audit/src/client/api.ts +185 -0
  192. package/modules/audit/src/client/contribution.tsrx +52 -0
  193. package/modules/audit/src/client/index.ts +23 -0
  194. package/modules/audit/src/client/navigation.ts +74 -0
  195. package/modules/audit/src/client/presentation.ts +105 -0
  196. package/modules/audit/src/client/state.ts +86 -0
  197. package/modules/audit/src/domain/data-classes.ts +16 -0
  198. package/modules/audit/src/domain/types.ts +339 -0
  199. package/modules/audit/src/index.ts +76 -0
  200. package/modules/audit/src/platform.ts +44 -0
  201. package/modules/audit/src/server/index.ts +177 -0
  202. package/modules/audit/src/server/runtime.ts +303 -0
  203. package/modules/audit/src/services/anchor-key.ts +143 -0
  204. package/modules/audit/src/services/anchor-rotation.ts +147 -0
  205. package/modules/audit/src/services/audit-runners.ts +278 -0
  206. package/modules/audit/src/services/backup-guard.ts +95 -0
  207. package/modules/audit/src/services/database-repository.ts +1946 -0
  208. package/modules/audit/src/services/declared-classes.ts +64 -0
  209. package/modules/audit/src/services/erasure-port.ts +126 -0
  210. package/modules/audit/src/services/erasure-service.ts +770 -0
  211. package/modules/audit/src/services/export-directory.ts +106 -0
  212. package/modules/audit/src/services/export-service.ts +714 -0
  213. package/modules/audit/src/services/hold-service.ts +295 -0
  214. package/modules/audit/src/services/index.ts +17 -0
  215. package/modules/audit/src/services/migration.ts +658 -0
  216. package/modules/audit/src/services/own-classes.ts +318 -0
  217. package/modules/audit/src/services/platform-version.ts +23 -0
  218. package/modules/audit/src/services/repository.ts +495 -0
  219. package/modules/audit/src/services/retention-service.ts +191 -0
  220. package/modules/audit/src/services/seal-service.ts +769 -0
  221. package/modules/audit/src/services/service-error.ts +61 -0
  222. package/modules/audit/src/services/subject-keys.ts +90 -0
  223. package/modules/audit/src/services/sweep-service.ts +298 -0
  224. package/modules/audit/src/services/zip.ts +217 -0
  225. package/modules/audit/src/settings.ts +69 -0
  226. package/modules/audit/translations/en.json +210 -0
  227. package/modules/audit/translations/pl.json +210 -0
  228. package/modules/auth/README.md +77 -29
  229. package/modules/auth/migrations/0016_external_identities.up.sql +16 -0
  230. package/modules/auth/migrations/0017_mfa_key_id.up.sql +8 -0
  231. package/modules/auth/migrations/0018_retire_bundled_module_scopes.up.sql +29 -0
  232. package/modules/auth/migrations/0019_notifications_member_scopes.up.sql +44 -0
  233. package/modules/auth/migrations/0020_identity_providers.up.sql +61 -0
  234. package/modules/auth/migrations/0021_membership_status.up.sql +19 -0
  235. package/modules/auth/migrations/0022_auth_provider_scopes.up.sql +45 -0
  236. package/modules/auth/migrations/0023_enterprise_module_scopes.down.sql +19 -0
  237. package/modules/auth/migrations/0023_enterprise_module_scopes.up.sql +71 -0
  238. package/modules/auth/migrations/0024_approvals_member_scopes.down.sql +22 -0
  239. package/modules/auth/migrations/0024_approvals_member_scopes.up.sql +48 -0
  240. package/modules/auth/migrations/0025_audit_holds_owner_scope.down.sql +21 -0
  241. package/modules/auth/migrations/0025_audit_holds_owner_scope.up.sql +47 -0
  242. package/modules/auth/migrations/0026_auth_export_keyset_indexes.down.sql +6 -0
  243. package/modules/auth/migrations/0026_auth_export_keyset_indexes.up.sql +15 -0
  244. package/modules/auth/migrations/0027_workflow_automation_profile_scopes.down.sql +34 -0
  245. package/modules/auth/migrations/0027_workflow_automation_profile_scopes.up.sql +72 -0
  246. package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.down.sql +6 -0
  247. package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.up.sql +21 -0
  248. package/modules/auth/migrations/0029_reports_exports_access_scopes.down.sql +22 -0
  249. package/modules/auth/migrations/0029_reports_exports_access_scopes.up.sql +56 -0
  250. package/modules/auth/migrations/0030_auth_membership_keyset_index.down.sql +4 -0
  251. package/modules/auth/migrations/0030_auth_membership_keyset_index.up.sql +10 -0
  252. package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.down.sql +4 -0
  253. package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.up.sql +31 -0
  254. package/modules/auth/migrations/0032_audit_actor_service.down.sql +9 -0
  255. package/modules/auth/migrations/0032_audit_actor_service.up.sql +10 -0
  256. package/modules/auth/migrations/0033_auth_account_keyset_indexes.down.sql +5 -0
  257. package/modules/auth/migrations/0033_auth_account_keyset_indexes.up.sql +23 -0
  258. package/modules/auth/module.json +3 -3
  259. package/modules/auth/package.json +3 -2
  260. package/modules/auth/spec/module.yaml +372 -3
  261. package/modules/auth/src/acl/scopes.ts +50 -6
  262. package/modules/auth/src/cli/commands.json +11 -0
  263. package/modules/auth/src/cli/greenfield.ts +2 -2
  264. package/modules/auth/src/cli/index.ts +14 -0
  265. package/modules/auth/src/cli/secrets.ts +114 -0
  266. package/modules/auth/src/client/AuthenticationCore.tsrx +61 -8
  267. package/modules/auth/src/client/api.ts +72 -6
  268. package/modules/auth/src/client/contribution.tsrx +23 -0
  269. package/modules/auth/src/client/index.ts +6 -0
  270. package/modules/auth/src/client/providers/IdentityProviderForm.tsrx +191 -0
  271. package/modules/auth/src/client/providers/IdentityProvidersView.tsrx +358 -0
  272. package/modules/auth/src/client/providers/api.ts +128 -0
  273. package/modules/auth/src/client/providers/provider-columns.tsrx +60 -0
  274. package/modules/auth/src/client/providers/state.ts +35 -0
  275. package/modules/auth/src/client/security/SecurityView.tsrx +9 -1
  276. package/modules/auth/src/client/state.ts +15 -1
  277. package/modules/auth/src/domain/types.ts +55 -0
  278. package/modules/auth/src/index.ts +11 -1
  279. package/modules/auth/src/middleware/authentication.ts +4 -0
  280. package/modules/auth/src/server/endpoints.ts +467 -171
  281. package/modules/auth/src/server/http.ts +3 -2
  282. package/modules/auth/src/server/index.ts +22 -1
  283. package/modules/auth/src/server/membership-endpoints.ts +62 -0
  284. package/modules/auth/src/server/mfa-enforcement.ts +156 -0
  285. package/modules/auth/src/server/oidc.ts +389 -0
  286. package/modules/auth/src/server/provider-endpoints.ts +246 -0
  287. package/modules/auth/src/server/provider-host.ts +77 -0
  288. package/modules/auth/src/server/runtime.ts +231 -55
  289. package/modules/auth/src/services/auth-service-error.ts +7 -2
  290. package/modules/auth/src/services/auth-service.ts +948 -51
  291. package/modules/auth/src/services/data-classes.ts +236 -0
  292. package/modules/auth/src/services/database-repository.ts +853 -71
  293. package/modules/auth/src/services/identity-provider-service.ts +571 -0
  294. package/modules/auth/src/services/mail-delivery.ts +16 -16
  295. package/modules/auth/src/services/mail-message.ts +54 -0
  296. package/modules/auth/src/services/mail-port.ts +24 -0
  297. package/modules/auth/src/services/mail-smtp.ts +85 -0
  298. package/modules/auth/src/services/mfa-rotation.ts +144 -0
  299. package/modules/auth/src/services/migration.ts +810 -0
  300. package/modules/auth/src/services/password.ts +14 -0
  301. package/modules/auth/src/services/provider-secret-rotation.ts +169 -0
  302. package/modules/auth/src/services/provider-secrets.ts +136 -0
  303. package/modules/auth/src/services/repository.ts +321 -5
  304. package/modules/auth/src/services/session-sweep-runner.ts +73 -0
  305. package/modules/auth/src/services/settings-store.ts +10 -83
  306. package/modules/auth/src/services/totp.ts +88 -62
  307. package/modules/auth/src/services/validation.ts +194 -7
  308. package/modules/auth/src/settings.ts +18 -0
  309. package/modules/auth/translations/en.json +84 -1
  310. package/modules/auth/translations/pl.json +84 -1
  311. package/modules/automations/migrations/0005_secret_rotation_inventory.down.sql +1 -0
  312. package/modules/automations/migrations/0005_secret_rotation_inventory.up.sql +6 -0
  313. package/modules/automations/migrations/0006_automations_list_sort_indexes.down.sql +4 -0
  314. package/modules/automations/migrations/0006_automations_list_sort_indexes.up.sql +11 -0
  315. package/modules/automations/module.json +24 -6
  316. package/modules/automations/package.json +5 -2
  317. package/modules/automations/spec/module.yaml +81 -7
  318. package/modules/automations/src/api/endpoints.ts +202 -28
  319. package/modules/automations/src/cli/commands.json +17 -0
  320. package/modules/automations/src/cli/index.ts +114 -0
  321. package/modules/automations/src/client/AutomationScheduleForm.tsrx +77 -19
  322. package/modules/automations/src/client/AutomationSchedulesView.tsrx +144 -40
  323. package/modules/automations/src/client/AutomationTriggersView.tsrx +131 -32
  324. package/modules/automations/src/client/api.ts +62 -19
  325. package/modules/automations/src/client/presentation.ts +20 -2
  326. package/modules/automations/src/client/state.ts +84 -0
  327. package/modules/automations/src/domain/cadence.ts +99 -3
  328. package/modules/automations/src/domain/cron.ts +366 -0
  329. package/modules/automations/src/domain/time-zone.ts +61 -0
  330. package/modules/automations/src/platform.ts +38 -4
  331. package/modules/automations/src/server/index.ts +11 -0
  332. package/modules/automations/src/server/runtime.ts +46 -22
  333. package/modules/{automations-workflows-integration/src/server/adapter.ts → automations/src/server/workflow-target.ts} +13 -14
  334. package/modules/{automations-workflows-integration/src/server/tools.ts → automations/src/server/workflow-tools.ts} +0 -0
  335. package/modules/automations/src/services/data-classes.ts +62 -0
  336. package/modules/automations/src/services/database-repository.ts +152 -38
  337. package/modules/automations/src/services/migration.ts +119 -9
  338. package/modules/automations/src/services/repository.ts +52 -1
  339. package/modules/automations/src/services/schedule-runner.ts +84 -0
  340. package/modules/automations/src/services/schedule-service.ts +152 -26
  341. package/modules/automations/src/services/secret-rotation.ts +176 -0
  342. package/modules/automations/src/services/secret-vault.ts +68 -41
  343. package/modules/automations/src/services/trigger-service.ts +13 -10
  344. package/modules/automations/translations/en.json +15 -2
  345. package/modules/automations/translations/pl.json +15 -2
  346. package/modules/connectors/migrations/0001_connectors_core.down.sql +3 -0
  347. package/modules/connectors/migrations/0001_connectors_core.up.sql +72 -0
  348. package/modules/connectors/migrations/0002_connectors_call_keys.down.sql +3 -0
  349. package/modules/connectors/migrations/0002_connectors_call_keys.up.sql +18 -0
  350. package/modules/connectors/migrations/README.md +3 -0
  351. package/modules/connectors/module.json +28 -0
  352. package/modules/{automations-workflows-integration → connectors}/package.json +27 -26
  353. package/modules/connectors/spec/module.yaml +447 -0
  354. package/modules/connectors/src/acl/permissions.ts +6 -0
  355. package/modules/connectors/src/agent/tools.ts +180 -0
  356. package/modules/connectors/src/api/endpoints.ts +572 -0
  357. package/modules/connectors/src/client/ConnectorCallsView.tsrx +358 -0
  358. package/modules/connectors/src/client/ConnectorForm.tsrx +424 -0
  359. package/modules/connectors/src/client/ConnectorsView.tsrx +711 -0
  360. package/modules/connectors/src/client/api.ts +281 -0
  361. package/modules/connectors/src/client/consent.ts +48 -0
  362. package/modules/connectors/src/client/contribution.tsrx +37 -0
  363. package/modules/connectors/src/client/index.ts +22 -0
  364. package/modules/connectors/src/client/navigation.ts +42 -0
  365. package/modules/connectors/src/client/presentation.ts +38 -0
  366. package/modules/connectors/src/client/state.ts +175 -0
  367. package/modules/connectors/src/domain/calls.ts +70 -0
  368. package/modules/connectors/src/domain/definitions.ts +190 -0
  369. package/modules/connectors/src/domain/http-json.ts +68 -0
  370. package/modules/connectors/src/domain/types.ts +185 -0
  371. package/modules/connectors/src/index.ts +64 -0
  372. package/modules/connectors/src/platform.ts +56 -0
  373. package/modules/connectors/src/server/index.ts +66 -0
  374. package/modules/connectors/src/server/runtime.ts +163 -0
  375. package/modules/connectors/src/services/call-service.ts +903 -0
  376. package/modules/connectors/src/services/connectors-service.ts +653 -0
  377. package/modules/connectors/src/services/credential-vault.ts +188 -0
  378. package/modules/connectors/src/services/data-classes.ts +158 -0
  379. package/modules/connectors/src/services/database-repository.ts +897 -0
  380. package/modules/connectors/src/services/egress.ts +261 -0
  381. package/modules/connectors/src/services/index.ts +19 -0
  382. package/modules/connectors/src/services/migration.ts +166 -0
  383. package/modules/connectors/src/services/repository.ts +175 -0
  384. package/modules/connectors/src/services/service-error.ts +46 -0
  385. package/modules/connectors/src/settings.ts +69 -0
  386. package/modules/connectors/translations/en.json +191 -0
  387. package/modules/connectors/translations/pl.json +191 -0
  388. package/modules/directory/migrations/0001_directory_core.down.sql +5 -0
  389. package/modules/directory/migrations/0001_directory_core.up.sql +111 -0
  390. package/modules/directory/migrations/README.md +3 -0
  391. package/modules/directory/module.json +27 -0
  392. package/modules/directory/package.json +46 -0
  393. package/modules/directory/spec/module.yaml +351 -0
  394. package/modules/directory/src/acl/permissions.ts +7 -0
  395. package/modules/directory/src/api/endpoints.ts +478 -0
  396. package/modules/directory/src/api/scim-endpoints.ts +516 -0
  397. package/modules/directory/src/client/GroupMappingsView.tsrx +430 -0
  398. package/modules/directory/src/client/ProvisioningLogView.tsrx +248 -0
  399. package/modules/directory/src/client/ScimTokensView.tsrx +587 -0
  400. package/modules/directory/src/client/api.ts +222 -0
  401. package/modules/directory/src/client/contribution.tsrx +46 -0
  402. package/modules/directory/src/client/index.ts +22 -0
  403. package/modules/directory/src/client/navigation.ts +57 -0
  404. package/modules/directory/src/client/presentation.ts +143 -0
  405. package/modules/directory/src/client/state.ts +173 -0
  406. package/modules/directory/src/domain/scim.ts +346 -0
  407. package/modules/directory/src/domain/types.ts +166 -0
  408. package/modules/directory/src/index.ts +44 -0
  409. package/modules/directory/src/platform.ts +43 -0
  410. package/modules/directory/src/server/index.ts +17 -0
  411. package/modules/directory/src/server/runtime.ts +124 -0
  412. package/modules/directory/src/services/auth-port.ts +127 -0
  413. package/modules/directory/src/services/data-classes.ts +41 -0
  414. package/modules/directory/src/services/database-repository.ts +1067 -0
  415. package/modules/directory/src/services/directory-service.ts +311 -0
  416. package/modules/directory/src/services/index.ts +43 -0
  417. package/modules/directory/src/services/migration.ts +146 -0
  418. package/modules/directory/src/services/provisioning-service.ts +1420 -0
  419. package/modules/directory/src/services/rate-limiter.ts +103 -0
  420. package/modules/directory/src/services/repository.ts +208 -0
  421. package/modules/directory/src/services/service-error.ts +42 -0
  422. package/modules/directory/src/services/token-service.ts +215 -0
  423. package/modules/directory/src/settings.ts +86 -0
  424. package/modules/directory/translations/en.json +185 -0
  425. package/modules/directory/translations/pl.json +185 -0
  426. package/modules/documents/migrations/0001_documents_core.down.sql +1 -0
  427. package/modules/documents/migrations/0001_documents_core.up.sql +35 -0
  428. package/modules/documents/migrations/0002_documents_files_page.down.sql +1 -0
  429. package/modules/documents/migrations/0002_documents_files_page.up.sql +6 -0
  430. package/modules/documents/migrations/README.md +3 -0
  431. package/modules/documents/module.json +28 -0
  432. package/modules/documents/package.json +49 -0
  433. package/modules/documents/spec/module.yaml +247 -0
  434. package/modules/documents/src/acl/permissions.ts +6 -0
  435. package/modules/documents/src/api/endpoints.ts +401 -0
  436. package/modules/documents/src/client/DocumentUploader.tsrx +148 -0
  437. package/modules/documents/src/client/DocumentsView.tsrx +540 -0
  438. package/modules/documents/src/client/api.ts +209 -0
  439. package/modules/documents/src/client/contribution.tsrx +29 -0
  440. package/modules/documents/src/client/download.ts +70 -0
  441. package/modules/documents/src/client/index.ts +22 -0
  442. package/modules/documents/src/client/navigation.ts +25 -0
  443. package/modules/documents/src/client/presentation.ts +110 -0
  444. package/modules/documents/src/client/state.ts +117 -0
  445. package/modules/documents/src/domain/attachments.ts +84 -0
  446. package/modules/documents/src/domain/types.ts +80 -0
  447. package/modules/documents/src/index.ts +46 -0
  448. package/modules/documents/src/platform.ts +61 -0
  449. package/modules/documents/src/server/index.ts +30 -0
  450. package/modules/documents/src/server/runtime.ts +107 -0
  451. package/modules/documents/src/services/attachments.ts +39 -0
  452. package/modules/documents/src/services/data-classes.ts +25 -0
  453. package/modules/documents/src/services/database-repository.ts +259 -0
  454. package/modules/documents/src/services/documents-service.ts +586 -0
  455. package/modules/documents/src/services/index.ts +18 -0
  456. package/modules/documents/src/services/migration.ts +82 -0
  457. package/modules/documents/src/services/repository.ts +48 -0
  458. package/modules/documents/src/settings.ts +107 -0
  459. package/modules/documents/translations/en.json +111 -0
  460. package/modules/documents/translations/pl.json +111 -0
  461. package/modules/exports/migrations/0001_exports_core.down.sql +2 -0
  462. package/modules/exports/migrations/0001_exports_core.up.sql +28 -0
  463. package/modules/exports/migrations/0002_exports_job_routing_role.down.sql +4 -0
  464. package/modules/exports/migrations/0002_exports_job_routing_role.up.sql +21 -0
  465. package/modules/exports/migrations/README.md +3 -0
  466. package/modules/exports/module.json +28 -0
  467. package/modules/exports/package.json +44 -0
  468. package/modules/exports/spec/module.yaml +229 -0
  469. package/modules/exports/src/acl/permissions.ts +6 -0
  470. package/modules/exports/src/api/endpoints.ts +211 -0
  471. package/modules/exports/src/client/ExportsView.tsrx +327 -0
  472. package/modules/exports/src/client/api.ts +133 -0
  473. package/modules/exports/src/client/contribution.tsrx +31 -0
  474. package/modules/exports/src/client/index.ts +20 -0
  475. package/modules/exports/src/client/navigation.ts +25 -0
  476. package/modules/exports/src/client/presentation.ts +53 -0
  477. package/modules/exports/src/client/state.ts +56 -0
  478. package/modules/exports/src/domain/lists.ts +29 -0
  479. package/modules/exports/src/domain/types.ts +120 -0
  480. package/modules/exports/src/index.ts +41 -0
  481. package/modules/exports/src/platform.ts +80 -0
  482. package/modules/exports/src/server/index.ts +51 -0
  483. package/modules/exports/src/server/runtime.ts +160 -0
  484. package/modules/exports/src/services/data-classes.ts +56 -0
  485. package/modules/exports/src/services/database-repository.ts +405 -0
  486. package/modules/exports/src/services/export-runner.ts +123 -0
  487. package/modules/exports/src/services/export-service.ts +356 -0
  488. package/modules/exports/src/services/index.ts +6 -0
  489. package/modules/exports/src/services/list-registry.ts +105 -0
  490. package/modules/exports/src/services/migration.ts +89 -0
  491. package/modules/exports/src/services/repository.ts +97 -0
  492. package/modules/exports/src/settings.ts +70 -0
  493. package/modules/exports/translations/en.json +69 -0
  494. package/modules/exports/translations/pl.json +69 -0
  495. package/modules/import/migrations/0001_import_core.down.sql +6 -0
  496. package/modules/import/migrations/0001_import_core.up.sql +61 -0
  497. package/modules/import/migrations/0002_import_job_routing_role.down.sql +4 -0
  498. package/modules/import/migrations/0002_import_job_routing_role.up.sql +20 -0
  499. package/modules/import/migrations/0003_import_jobs_export_index.down.sql +1 -0
  500. package/modules/import/migrations/0003_import_jobs_export_index.up.sql +7 -0
  501. package/modules/import/migrations/0004_import_jobs_traceparent.down.sql +1 -0
  502. package/modules/import/migrations/0004_import_jobs_traceparent.up.sql +7 -0
  503. package/modules/import/migrations/README.md +3 -0
  504. package/modules/import/module.json +37 -0
  505. package/modules/import/package.json +50 -0
  506. package/modules/import/spec/module.yaml +335 -0
  507. package/modules/import/src/acl/permissions.ts +6 -0
  508. package/modules/import/src/api/endpoints.ts +400 -0
  509. package/modules/import/src/client/ImportJobDrawer.tsrx +229 -0
  510. package/modules/import/src/client/ImportsView.tsrx +455 -0
  511. package/modules/import/src/client/NewImportForm.tsrx +274 -0
  512. package/modules/import/src/client/api.ts +243 -0
  513. package/modules/import/src/client/contribution.tsrx +29 -0
  514. package/modules/import/src/client/index.ts +23 -0
  515. package/modules/import/src/client/navigation.ts +25 -0
  516. package/modules/import/src/client/presentation.ts +59 -0
  517. package/modules/import/src/client/state.ts +43 -0
  518. package/modules/import/src/domain/csv.ts +258 -0
  519. package/modules/import/src/domain/ports.ts +157 -0
  520. package/modules/import/src/domain/types.ts +179 -0
  521. package/modules/import/src/index.ts +58 -0
  522. package/modules/import/src/platform.ts +60 -0
  523. package/modules/import/src/server/index.ts +49 -0
  524. package/modules/import/src/server/runtime.ts +158 -0
  525. package/modules/import/src/services/csv-source.ts +121 -0
  526. package/modules/import/src/services/data-classes.ts +63 -0
  527. package/modules/import/src/services/database-repository.ts +780 -0
  528. package/modules/import/src/services/import-runner.ts +127 -0
  529. package/modules/import/src/services/import-service.ts +872 -0
  530. package/modules/import/src/services/index.ts +6 -0
  531. package/modules/import/src/services/migration.ts +156 -0
  532. package/modules/import/src/services/port-registry.ts +158 -0
  533. package/modules/import/src/services/repository.ts +147 -0
  534. package/modules/import/src/settings.ts +70 -0
  535. package/modules/import/translations/en.json +175 -0
  536. package/modules/import/translations/pl.json +175 -0
  537. package/modules/metering/migrations/0001_metering_core.down.sql +6 -0
  538. package/modules/metering/migrations/0001_metering_core.up.sql +119 -0
  539. package/modules/metering/migrations/0002_metering_bucket_export.down.sql +3 -0
  540. package/modules/metering/migrations/0002_metering_bucket_export.up.sql +5 -0
  541. package/modules/metering/migrations/README.md +3 -0
  542. package/modules/metering/module.json +46 -0
  543. package/modules/metering/package.json +50 -0
  544. package/modules/metering/spec/module.yaml +269 -0
  545. package/modules/metering/src/acl/permissions.ts +5 -0
  546. package/modules/metering/src/api/endpoints.ts +104 -0
  547. package/modules/metering/src/cli/commands.json +28 -0
  548. package/modules/metering/src/cli/index.ts +246 -0
  549. package/modules/metering/src/client/LimitsView.tsrx +170 -0
  550. package/modules/metering/src/client/UsageSummaryWidget.tsrx +63 -0
  551. package/modules/metering/src/client/UsageView.tsrx +205 -0
  552. package/modules/metering/src/client/api.ts +91 -0
  553. package/modules/metering/src/client/contribution.tsrx +42 -0
  554. package/modules/metering/src/client/index.ts +26 -0
  555. package/modules/metering/src/client/navigation.ts +54 -0
  556. package/modules/metering/src/client/presentation.ts +63 -0
  557. package/modules/metering/src/client/state.ts +31 -0
  558. package/modules/metering/src/domain/meters.ts +100 -0
  559. package/modules/metering/src/domain/types.ts +71 -0
  560. package/modules/metering/src/index.ts +51 -0
  561. package/modules/metering/src/platform.ts +84 -0
  562. package/modules/metering/src/server/index.ts +40 -0
  563. package/modules/metering/src/server/runtime.ts +165 -0
  564. package/modules/metering/src/services/data-classes.ts +34 -0
  565. package/modules/metering/src/services/database-repository.ts +574 -0
  566. package/modules/metering/src/services/index.ts +9 -0
  567. package/modules/metering/src/services/meter-registry.ts +145 -0
  568. package/modules/metering/src/services/metering-service.ts +441 -0
  569. package/modules/metering/src/services/migration.ts +183 -0
  570. package/modules/metering/src/services/notifications.ts +75 -0
  571. package/modules/metering/src/services/reports.ts +105 -0
  572. package/modules/metering/src/services/repository.ts +132 -0
  573. package/modules/metering/src/services/service-error.ts +46 -0
  574. package/modules/metering/src/settings.ts +47 -0
  575. package/modules/metering/translations/en.json +68 -0
  576. package/modules/metering/translations/pl.json +68 -0
  577. package/modules/notifications/migrations/0001_notifications_core.down.sql +1 -0
  578. package/modules/notifications/migrations/0001_notifications_core.up.sql +26 -0
  579. package/modules/notifications/migrations/0002_notifications_preferences.down.sql +1 -0
  580. package/modules/notifications/migrations/0002_notifications_preferences.up.sql +17 -0
  581. package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.down.sql +1 -0
  582. package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.up.sql +30 -0
  583. package/modules/notifications/migrations/0004_notifications_deliveries.down.sql +1 -0
  584. package/modules/notifications/migrations/0004_notifications_deliveries.up.sql +35 -0
  585. package/modules/notifications/migrations/0005_notifications_delivery_routing_role.down.sql +2 -0
  586. package/modules/notifications/migrations/0005_notifications_delivery_routing_role.up.sql +17 -0
  587. package/modules/notifications/migrations/0006_notifications_delivery_title.down.sql +1 -0
  588. package/modules/notifications/migrations/0006_notifications_delivery_title.up.sql +11 -0
  589. package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.down.sql +2 -0
  590. package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.up.sql +19 -0
  591. package/modules/notifications/migrations/0008_notifications_delivery_claim.down.sql +8 -0
  592. package/modules/notifications/migrations/0008_notifications_delivery_claim.up.sql +19 -0
  593. package/modules/notifications/migrations/0009_notifications_kind_approvals.down.sql +16 -0
  594. package/modules/notifications/migrations/0009_notifications_kind_approvals.up.sql +18 -0
  595. package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.down.sql +16 -0
  596. package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.up.sql +18 -0
  597. package/modules/notifications/migrations/0011_notifications_retention_keyset.down.sql +5 -0
  598. package/modules/notifications/migrations/0011_notifications_retention_keyset.up.sql +15 -0
  599. package/modules/notifications/migrations/0012_notifications_email_channel.down.sql +12 -0
  600. package/modules/notifications/migrations/0012_notifications_email_channel.up.sql +38 -0
  601. package/modules/notifications/migrations/0013_notifications_list_pages.down.sql +5 -0
  602. package/modules/notifications/migrations/0013_notifications_list_pages.up.sql +13 -0
  603. package/modules/notifications/migrations/README.md +3 -0
  604. package/modules/notifications/module.json +32 -0
  605. package/modules/notifications/package.json +49 -0
  606. package/modules/notifications/spec/module.yaml +718 -0
  607. package/modules/notifications/src/acl/permissions.ts +11 -0
  608. package/modules/notifications/src/api/endpoints.ts +814 -0
  609. package/modules/notifications/src/cli/commands.json +17 -0
  610. package/modules/notifications/src/cli/index.ts +126 -0
  611. package/modules/notifications/src/client/DeliveriesView.tsrx +483 -0
  612. package/modules/notifications/src/client/FactList.tsrx +24 -0
  613. package/modules/notifications/src/client/NotificationPreferencesView.tsrx +240 -0
  614. package/modules/notifications/src/client/NotificationsInboxView.tsrx +592 -0
  615. package/modules/notifications/src/client/UnreadCountWidget.tsrx +62 -0
  616. package/modules/notifications/src/client/WebhookForm.tsrx +297 -0
  617. package/modules/notifications/src/client/WebhooksView.tsrx +637 -0
  618. package/modules/notifications/src/client/api.ts +382 -0
  619. package/modules/notifications/src/client/contribution.tsrx +83 -0
  620. package/modules/notifications/src/client/inbox.ts +41 -0
  621. package/modules/notifications/src/client/index.ts +29 -0
  622. package/modules/notifications/src/client/navigation.ts +92 -0
  623. package/modules/notifications/src/client/notifications.css +70 -0
  624. package/modules/notifications/src/client/presentation.ts +81 -0
  625. package/modules/notifications/src/client/state.ts +246 -0
  626. package/modules/notifications/src/client/unread-count.ts +26 -0
  627. package/modules/notifications/src/domain/locale.ts +47 -0
  628. package/modules/notifications/src/domain/publish.ts +46 -0
  629. package/modules/notifications/src/domain/types.ts +186 -0
  630. package/modules/notifications/src/index.ts +62 -0
  631. package/modules/notifications/src/platform.ts +85 -0
  632. package/modules/notifications/src/server/index.ts +82 -0
  633. package/modules/notifications/src/server/runtime.ts +251 -0
  634. package/modules/notifications/src/services/data-classes.ts +139 -0
  635. package/modules/notifications/src/services/database-repository.ts +1334 -0
  636. package/modules/notifications/src/services/delivery-payload.ts +93 -0
  637. package/modules/notifications/src/services/delivery-runner.ts +170 -0
  638. package/modules/notifications/src/services/delivery-service.ts +811 -0
  639. package/modules/notifications/src/services/egress.ts +239 -0
  640. package/modules/notifications/src/services/email-channel.ts +93 -0
  641. package/modules/notifications/src/services/index.ts +23 -0
  642. package/modules/notifications/src/services/migration.ts +572 -0
  643. package/modules/notifications/src/services/notifications-service.ts +297 -0
  644. package/modules/notifications/src/services/paging.ts +60 -0
  645. package/modules/notifications/src/services/publish-service.ts +133 -0
  646. package/modules/notifications/src/services/repository.ts +342 -0
  647. package/modules/notifications/src/services/secret-rotation.ts +177 -0
  648. package/modules/notifications/src/services/secret-vault.ts +168 -0
  649. package/modules/notifications/src/services/service-error.ts +67 -0
  650. package/modules/notifications/src/services/signature.ts +81 -0
  651. package/modules/notifications/src/services/webhook-service.ts +307 -0
  652. package/modules/notifications/src/settings.ts +130 -0
  653. package/modules/notifications/translations/en.json +255 -0
  654. package/modules/notifications/translations/pl.json +255 -0
  655. package/modules/profile/module.json +3 -3
  656. package/modules/profile/package.json +2 -2
  657. package/modules/profile/spec/module.yaml +3 -3
  658. package/modules/profile/src/services/database-repository.ts +3 -13
  659. package/modules/reports/module.json +28 -0
  660. package/modules/reports/package.json +42 -0
  661. package/modules/reports/spec/module.yaml +231 -0
  662. package/modules/reports/src/acl/permissions.ts +5 -0
  663. package/modules/reports/src/api/endpoints.ts +72 -0
  664. package/modules/reports/src/client/ReportsSummaryWidget.tsrx +52 -0
  665. package/modules/reports/src/client/ReportsView.tsrx +246 -0
  666. package/modules/reports/src/client/api.ts +65 -0
  667. package/modules/reports/src/client/contribution.tsrx +36 -0
  668. package/modules/reports/src/client/index.ts +18 -0
  669. package/modules/reports/src/client/navigation.ts +39 -0
  670. package/modules/reports/src/client/presentation.ts +123 -0
  671. package/modules/reports/src/client/state.ts +28 -0
  672. package/modules/reports/src/domain/providers.ts +115 -0
  673. package/modules/reports/src/domain/types.ts +44 -0
  674. package/modules/reports/src/index.ts +47 -0
  675. package/modules/reports/src/platform.ts +37 -0
  676. package/modules/reports/src/server/index.ts +21 -0
  677. package/modules/reports/src/server/runtime.ts +40 -0
  678. package/modules/reports/src/services/index.ts +17 -0
  679. package/modules/reports/src/services/provider-registry.ts +144 -0
  680. package/modules/reports/src/services/range.ts +65 -0
  681. package/modules/reports/src/services/reports-service.ts +316 -0
  682. package/modules/reports/src/services/service-error.ts +26 -0
  683. package/modules/reports/src/settings.ts +49 -0
  684. package/modules/reports/translations/en.json +35 -0
  685. package/modules/reports/translations/pl.json +35 -0
  686. package/modules/sandbox/module.json +4 -4
  687. package/modules/sandbox/package.json +2 -2
  688. package/modules/sandbox/spec/module.yaml +3 -3
  689. package/modules/sandbox/src/services/database-repository.ts +14 -23
  690. package/modules/sandbox/src/services/directory.ts +16 -0
  691. package/modules/sandbox/src/services/sandbox-service.ts +13 -11
  692. package/modules/search/migrations/0001_search_core.down.sql +1 -0
  693. package/modules/search/migrations/0001_search_core.up.sql +22 -0
  694. package/modules/search/migrations/0002_search_recent_export.down.sql +3 -0
  695. package/modules/search/migrations/0002_search_recent_export.up.sql +5 -0
  696. package/modules/search/migrations/README.md +3 -0
  697. package/modules/search/module.json +28 -0
  698. package/modules/search/package.json +43 -0
  699. package/modules/search/spec/module.yaml +177 -0
  700. package/modules/search/src/acl/permissions.ts +5 -0
  701. package/modules/search/src/api/endpoints.ts +221 -0
  702. package/modules/search/src/client/SearchView.tsrx +346 -0
  703. package/modules/search/src/client/api.ts +157 -0
  704. package/modules/search/src/client/contribution.tsrx +31 -0
  705. package/modules/search/src/client/index.ts +17 -0
  706. package/modules/search/src/client/navigation.ts +97 -0
  707. package/modules/search/src/client/search.css +56 -0
  708. package/modules/search/src/client/state.ts +113 -0
  709. package/modules/search/src/domain/data-classes.ts +73 -0
  710. package/modules/search/src/domain/providers.ts +88 -0
  711. package/modules/search/src/domain/types.ts +56 -0
  712. package/modules/search/src/index.ts +46 -0
  713. package/modules/search/src/platform.ts +42 -0
  714. package/modules/search/src/server/index.ts +25 -0
  715. package/modules/search/src/server/runtime.ts +123 -0
  716. package/modules/search/src/services/database-repository.ts +163 -0
  717. package/modules/search/src/services/index.ts +21 -0
  718. package/modules/search/src/services/migration.ts +65 -0
  719. package/modules/search/src/services/provider-registry.ts +137 -0
  720. package/modules/search/src/services/repository.ts +43 -0
  721. package/modules/search/src/services/search-service.ts +442 -0
  722. package/modules/search/src/services/service-error.ts +35 -0
  723. package/modules/search/src/settings.ts +68 -0
  724. package/modules/search/translations/en.json +45 -0
  725. package/modules/search/translations/pl.json +45 -0
  726. package/modules/system/module.json +2 -2
  727. package/modules/system/package.json +2 -2
  728. package/modules/system/spec/module.yaml +30 -3
  729. package/modules/system/src/client/FlagsPanel.tsrx +127 -0
  730. package/modules/system/src/client/ModuleSettingRow.tsrx +17 -7
  731. package/modules/system/src/client/ModuleSettingsSection.tsrx +10 -71
  732. package/modules/system/src/client/ModulesView.tsrx +91 -65
  733. package/modules/system/src/client/flags.ts +35 -0
  734. package/modules/system/src/client/index.ts +8 -0
  735. package/modules/system/src/client/navigation.ts +8 -0
  736. package/modules/system/src/client/settings-state.ts +159 -0
  737. package/modules/system/src/client/state.ts +2 -0
  738. package/modules/system/src/domain/time-zone.ts +105 -0
  739. package/modules/system/src/index.ts +12 -0
  740. package/modules/system/src/platform.ts +5 -1
  741. package/modules/system/src/server/endpoints.ts +72 -21
  742. package/modules/system/src/settings.ts +28 -0
  743. package/modules/system/translations/en.json +17 -0
  744. package/modules/system/translations/pl.json +17 -0
  745. package/modules/users/module.json +30 -4
  746. package/modules/users/package.json +5 -2
  747. package/modules/users/spec/module.yaml +290 -7
  748. package/modules/users/src/api/endpoints.ts +228 -15
  749. package/modules/users/src/client/InviteMemberForm.tsrx +2 -2
  750. package/modules/users/src/client/MemberDrawer.tsrx +142 -26
  751. package/modules/users/src/client/UserAccountForm.tsrx +2 -2
  752. package/modules/users/src/client/UsersView.tsrx +432 -71
  753. package/modules/users/src/client/api.ts +106 -12
  754. package/modules/users/src/client/contribution.tsrx +5 -0
  755. package/modules/users/src/client/member-columns.tsrx +22 -9
  756. package/modules/users/src/client/state.ts +172 -7
  757. package/modules/users/src/domain/lists.ts +9 -0
  758. package/modules/users/src/index.ts +11 -0
  759. package/modules/users/src/platform.ts +50 -1
  760. package/modules/users/src/services/member-export.ts +51 -0
  761. package/modules/users/src/services/member-import.ts +249 -0
  762. package/modules/users/src/services/member-search.ts +145 -0
  763. package/modules/users/src/services/users-service.ts +117 -4
  764. package/modules/users/translations/en.json +57 -8
  765. package/modules/users/translations/pl.json +57 -8
  766. package/modules/workflows/migrations/0004_payload_rotation_inventory.up.sql +17 -0
  767. package/modules/workflows/migrations/0005_workflows_human_approval.up.sql +28 -0
  768. package/modules/workflows/migrations/0006_workflows_retention_indexes.up.sql +11 -0
  769. package/modules/workflows/migrations/0007_workflows_erasure_subject_indexes.up.sql +17 -0
  770. package/modules/workflows/migrations/0008_workflows_subject_account.up.sql +24 -0
  771. package/modules/workflows/migrations/0009_workflows_definition_list_indexes.up.sql +9 -0
  772. package/modules/workflows/module.json +34 -6
  773. package/modules/workflows/package.json +3 -2
  774. package/modules/workflows/spec/module.yaml +26 -9
  775. package/modules/workflows/src/api/endpoints.ts +131 -25
  776. package/modules/workflows/src/cli/commands.json +17 -0
  777. package/modules/workflows/src/cli/index.ts +114 -0
  778. package/modules/workflows/src/client/WorkflowExecutionHistory.tsrx +36 -27
  779. package/modules/workflows/src/client/WorkflowInspector.tsrx +116 -0
  780. package/modules/workflows/src/client/WorkflowNodePalette.tsrx +1 -0
  781. package/modules/workflows/src/client/WorkflowsView.tsrx +236 -99
  782. package/modules/workflows/src/client/api.ts +54 -34
  783. package/modules/workflows/src/client/canvas-model.ts +12 -0
  784. package/modules/workflows/src/client/state.ts +95 -0
  785. package/modules/workflows/src/domain/events.ts +25 -11
  786. package/modules/workflows/src/domain/graph.ts +135 -1
  787. package/modules/workflows/src/domain/types.ts +91 -4
  788. package/modules/workflows/src/platform.ts +12 -0
  789. package/modules/workflows/src/server/runtime.ts +78 -5
  790. package/modules/workflows/src/services/approvals.ts +62 -0
  791. package/modules/workflows/src/services/cursors.ts +37 -0
  792. package/modules/workflows/src/services/data-classes.ts +232 -0
  793. package/modules/workflows/src/services/database-repository.ts +660 -171
  794. package/modules/workflows/src/services/migration.ts +240 -0
  795. package/modules/workflows/src/services/notifications.ts +98 -0
  796. package/modules/workflows/src/services/payload-codec.ts +51 -28
  797. package/modules/workflows/src/services/payload-rotation.ts +171 -0
  798. package/modules/workflows/src/services/repository.ts +123 -8
  799. package/modules/workflows/src/services/simulation.ts +7 -0
  800. package/modules/workflows/src/services/worker.ts +446 -25
  801. package/modules/workflows/src/services/workflows-service.ts +264 -103
  802. package/modules/workflows/translations/en.json +12 -4
  803. package/modules/workflows/translations/pl.json +12 -4
  804. package/modules.json +48 -4
  805. package/package.json +66 -7
  806. package/packages/client/package.json +2 -1
  807. package/packages/client/src/ApplicationShell.tsrx +43 -0
  808. package/packages/client/src/contributions.ts +55 -0
  809. package/packages/client/src/i18n/locales/en.json +5 -0
  810. package/packages/client/src/i18n/locales/pl.json +5 -0
  811. package/packages/client/src/index.ts +13 -1
  812. package/packages/client/src/routing.ts +22 -0
  813. package/packages/client/src/shell/CommandPalette.tsrx +223 -11
  814. package/packages/client/src/shell/command-search.ts +126 -0
  815. package/packages/client/src/shell/command.ts +30 -0
  816. package/packages/client/src/shell/navigation.ts +12 -0
  817. package/packages/client/src/shell/palette-keys.ts +27 -0
  818. package/packages/client/src/shell/shell.css +7 -0
  819. package/packages/contracts/schemas/module-spec.schema.json +287 -1
  820. package/packages/contracts/schemas/module.schema.json +25 -0
  821. package/packages/contracts/src/index.ts +144 -1
  822. package/packages/database/src/backup.ts +179 -0
  823. package/packages/database/src/decoders.ts +25 -0
  824. package/packages/database/src/index.ts +17 -0
  825. package/packages/database/src/record-history.ts +5 -14
  826. package/packages/dev-console/package.json +2 -1
  827. package/packages/dev-console/src/brand.d.mts +8 -0
  828. package/packages/dev-console/src/brand.mjs +33 -0
  829. package/packages/dev-console/src/index.mjs +2 -1
  830. package/packages/harness/src/index.ts +2 -0
  831. package/packages/harness/src/runtime.ts +218 -1
  832. package/packages/harness/src/tool-adapters.ts +19 -1
  833. package/packages/kernel/src/acl.ts +179 -0
  834. package/packages/kernel/src/capability-registry.ts +69 -2
  835. package/packages/kernel/src/data-class-registry.ts +354 -0
  836. package/packages/kernel/src/index.ts +65 -4
  837. package/packages/kernel/src/keyring.ts +208 -0
  838. package/packages/kernel/src/module-compatibility.ts +35 -4
  839. package/packages/kernel/src/module-registry.ts +49 -2
  840. package/packages/kernel/src/module-settings.ts +189 -27
  841. package/packages/server/package.json +2 -1
  842. package/packages/server/src/composition.ts +38 -0
  843. package/packages/server/src/endpoint.ts +104 -47
  844. package/packages/server/src/export/csv.ts +34 -0
  845. package/packages/server/src/export/definition.ts +346 -0
  846. package/packages/server/src/export/index.ts +61 -0
  847. package/packages/server/src/export/run.ts +116 -0
  848. package/packages/server/src/index.ts +155 -0
  849. package/packages/server/src/jobs/index.ts +93 -0
  850. package/packages/server/src/jobs/runner.ts +486 -0
  851. package/packages/server/src/log.ts +291 -0
  852. package/packages/server/src/mail/config.ts +184 -0
  853. package/packages/server/src/mail/contracts.ts +251 -0
  854. package/packages/server/src/mail/index.ts +31 -0
  855. package/packages/server/src/mail/port.ts +101 -0
  856. package/packages/server/src/mail/smtp.ts +191 -0
  857. package/packages/server/src/mail/template.ts +105 -0
  858. package/packages/server/src/metrics.ts +447 -0
  859. package/packages/server/src/pagination.ts +246 -0
  860. package/packages/server/src/trace/context.ts +103 -0
  861. package/packages/server/src/trace/egress.ts +66 -0
  862. package/packages/server/src/trace/error-sink.ts +296 -0
  863. package/packages/server/src/trace/exporter.ts +295 -0
  864. package/packages/server/src/trace/index.ts +60 -0
  865. package/packages/server/src/trace/job-sink.ts +172 -0
  866. package/packages/server/src/trace/tracer.ts +338 -0
  867. package/packages/server/src/web.ts +5 -1
  868. package/packages/storage/package.json +40 -0
  869. package/packages/storage/src/config.ts +189 -0
  870. package/packages/storage/src/content-type.ts +124 -0
  871. package/packages/storage/src/contracts.ts +112 -0
  872. package/packages/storage/src/envelope.ts +189 -0
  873. package/packages/storage/src/index.ts +43 -0
  874. package/packages/storage/src/local.ts +75 -0
  875. package/packages/storage/src/port.ts +251 -0
  876. package/packages/storage/src/read-token.ts +130 -0
  877. package/packages/storage/src/s3.ts +144 -0
  878. package/packages/storage/src/scanner.ts +22 -0
  879. package/packages/storage/src/sigv4.ts +104 -0
  880. package/packages/storage/src/store.ts +13 -0
  881. package/packages/ui/package.json +1 -0
  882. package/packages/ui/src/components/Button.tsrx +3 -0
  883. package/packages/ui/src/components/ConfirmDialog.tsrx +13 -2
  884. package/packages/ui/src/components/DateField.tsrx +92 -0
  885. package/packages/ui/src/components/DatePicker.tsrx +525 -0
  886. package/packages/ui/src/components/DateRangeField.tsrx +93 -0
  887. package/packages/ui/src/components/Drawer.tsrx +10 -1
  888. package/packages/ui/src/components/FileUpload.tsrx +158 -0
  889. package/packages/ui/src/components/Pagination.tsrx +130 -0
  890. package/packages/ui/src/components/Select.tsrx +78 -0
  891. package/packages/ui/src/components/Table.tsrx +498 -81
  892. package/packages/ui/src/components/Tabs.tsrx +53 -0
  893. package/packages/ui/src/components/ToastHost.tsrx +48 -0
  894. package/packages/ui/src/components/calendar.ts +243 -0
  895. package/packages/ui/src/components/date-field.ts +62 -0
  896. package/packages/ui/src/components/file-upload.ts +49 -0
  897. package/packages/ui/src/components/focus-trap.ts +62 -0
  898. package/packages/ui/src/components/pagination.ts +68 -0
  899. package/packages/ui/src/components/table-sorting.ts +49 -0
  900. package/packages/ui/src/components/tabs.ts +61 -0
  901. package/packages/ui/src/components/toast-store.ts +116 -0
  902. package/packages/ui/src/icons/Icon.tsrx +5 -0
  903. package/packages/ui/src/index.ts +45 -0
  904. package/packages/ui/src/styles/components.css +598 -0
  905. package/modules/automations-workflows-integration/module.json +0 -27
  906. package/modules/automations-workflows-integration/spec/module.yaml +0 -93
  907. package/modules/automations-workflows-integration/src/index.ts +0 -14
  908. package/modules/automations-workflows-integration/src/platform.ts +0 -24
  909. package/modules/automations-workflows-integration/src/server/index.ts +0 -11
  910. package/modules/automations-workflows-integration/translations/en.json +0 -21
  911. package/modules/automations-workflows-integration/translations/pl.json +0 -21
  912. package/modules/workflows/src/services/cursor-codec.ts +0 -62
@@ -0,0 +1,1067 @@
1
+ import type {
2
+ DatabaseHandle,
3
+ DatabaseParameter,
4
+ DatabaseTransaction,
5
+ } from '@flowdular/sdk/database';
6
+ import { integer, runDatabaseMigrations } from '@flowdular/sdk/database';
7
+ import { keysetWhere } from '@flowdular/sdk/server';
8
+ import type {
9
+ GroupListQuery,
10
+ ListDirection,
11
+ ListPage,
12
+ ListPosition,
13
+ ProvisioningEvent,
14
+ ProvisioningEventQuery,
15
+ ScimGroupMapping,
16
+ ScimToken,
17
+ ScimUserMapping,
18
+ TokenListQuery,
19
+ } from '../domain/types.ts';
20
+ import { databaseMigrations } from './migration.ts';
21
+ import type {
22
+ DirectoryRepository,
23
+ GroupMemberChange,
24
+ GroupMemberRow,
25
+ GroupMemberStep,
26
+ ResolvedGroupMember,
27
+ ScimGroupFilter,
28
+ ScimPageRequest,
29
+ ScimSlice,
30
+ ScimTokenSecret,
31
+ ScimUserFilter,
32
+ } from './repository.ts';
33
+
34
+ interface TokenRow {
35
+ id: string;
36
+ tenant_id: string;
37
+ label: string;
38
+ token_fingerprint: string;
39
+ token_hash: string;
40
+ status: ScimToken['status'];
41
+ created_by: string;
42
+ created_at: number | bigint | string;
43
+ last_used_at: number | bigint | string | null;
44
+ expires_at: number | bigint | string | null;
45
+ revoked_at: number | bigint | string | null;
46
+ }
47
+
48
+ interface UserRow {
49
+ id: string;
50
+ tenant_id: string;
51
+ external_id: string | null;
52
+ user_name: string;
53
+ account_id: string;
54
+ active: number | string;
55
+ created_at: number | bigint | string;
56
+ last_synced_at: number | bigint | string;
57
+ }
58
+
59
+ interface GroupRow {
60
+ id: string;
61
+ tenant_id: string;
62
+ external_id: string | null;
63
+ display_name: string;
64
+ role_key: string | null;
65
+ precedence: number | string;
66
+ member_count: number | bigint | string;
67
+ created_at: number | bigint | string;
68
+ updated_at: number | bigint | string;
69
+ }
70
+
71
+ interface EventRow {
72
+ id: string;
73
+ sequence: number | bigint | string;
74
+ tenant_id: string;
75
+ token_id: string;
76
+ operation: ProvisioningEvent['operation'];
77
+ subject: string;
78
+ outcome: ProvisioningEvent['outcome'];
79
+ reason: string | null;
80
+ occurred_at: number | bigint | string;
81
+ }
82
+
83
+ interface CountRow {
84
+ total: number | bigint | string;
85
+ }
86
+
87
+ /** A screen page row carries the value it was ordered by beside its columns. */
88
+ interface SortedRow {
89
+ id: string;
90
+ sort_key: string | number | bigint;
91
+ }
92
+
93
+ interface ResolvedRoleRow {
94
+ user_id: string;
95
+ account_id: string;
96
+ role_key: string | null;
97
+ }
98
+
99
+ /** Rows one provisioning event insert carries, so one statement stays bounded. */
100
+ const EVENT_INSERT_BATCH = 200;
101
+
102
+ /** The unique indexes a caller can actually collide with. */
103
+ export const DIRECTORY_UNIQUE_INDEXES = {
104
+ tokenLabel: 'directory_scim_tokens_label_idx',
105
+ userName: 'directory_scim_users_user_name_idx',
106
+ userAccount: 'directory_scim_users_account_idx',
107
+ groupDisplayName: 'directory_scim_groups_display_name_idx',
108
+ } as const;
109
+
110
+ /** A unique violation, translated at the boundary so no driver text escapes. */
111
+ export class DirectoryUniqueViolation extends Error {
112
+ constructor(readonly index: string) {
113
+ super('A unique index rejected the write.');
114
+ this.name = 'DirectoryUniqueViolation';
115
+ }
116
+ }
117
+
118
+ /* 23505 is the SQLSTATE for a unique violation; the index name keeps a
119
+ different unique index on the same table from being mistaken for it. */
120
+ function uniqueIndexOf(error: unknown): string | null {
121
+ const cause = error as { code?: unknown; constraint?: unknown };
122
+ const text = String(error);
123
+ if (cause?.code !== '23505' && !text.includes('23505')) return null;
124
+ const constraint = String(cause?.constraint ?? '');
125
+ return (
126
+ Object.values(DIRECTORY_UNIQUE_INDEXES).find(
127
+ (index) => constraint.includes(index) || text.includes(index),
128
+ ) ?? null
129
+ );
130
+ }
131
+
132
+ async function translatingUniqueViolation<T>(
133
+ run: () => Promise<T>,
134
+ ): Promise<T> {
135
+ try {
136
+ return await run();
137
+ } catch (error) {
138
+ const index = uniqueIndexOf(error);
139
+ if (index) throw new DirectoryUniqueViolation(index);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ function optionalInteger(
145
+ value: number | bigint | string | null,
146
+ field: string,
147
+ ): number | null {
148
+ return value === null ? null : integer(value, field);
149
+ }
150
+
151
+ const TOKEN_COLUMNS = `id, tenant_id, label, token_fingerprint, token_hash, status,
152
+ created_by, created_at, last_used_at, expires_at, revoked_at`;
153
+
154
+ const USER_COLUMNS = `id, tenant_id, external_id, user_name, account_id, active,
155
+ created_at, last_synced_at`;
156
+
157
+ /* The member count is derived on read: a stored counter would be a second
158
+ source of truth for the same rows. */
159
+ const GROUP_COLUMNS = `g.id, g.tenant_id, g.external_id, g.display_name, g.role_key,
160
+ g.precedence, g.created_at, g.updated_at,
161
+ (SELECT count(*) FROM directory_scim_group_members m
162
+ WHERE m.tenant_id = g.tenant_id AND m.group_id = g.id) AS member_count`;
163
+
164
+ function tokenFromRow(row: TokenRow): ScimTokenSecret {
165
+ return {
166
+ id: row.id,
167
+ tenantId: row.tenant_id,
168
+ label: row.label,
169
+ tokenFingerprint: row.token_fingerprint,
170
+ tokenHash: row.token_hash,
171
+ status: row.status,
172
+ createdBy: row.created_by,
173
+ createdAt: integer(row.created_at, 'created_at'),
174
+ lastUsedAt: optionalInteger(row.last_used_at, 'last_used_at'),
175
+ expiresAt: optionalInteger(row.expires_at, 'expires_at'),
176
+ revokedAt: optionalInteger(row.revoked_at, 'revoked_at'),
177
+ };
178
+ }
179
+
180
+ function withoutSecret(record: ScimTokenSecret): ScimToken {
181
+ const { tokenHash: _hash, ...visible } = record;
182
+ return visible;
183
+ }
184
+
185
+ function userFromRow(row: UserRow): ScimUserMapping {
186
+ return {
187
+ id: row.id,
188
+ tenantId: row.tenant_id,
189
+ externalId: row.external_id,
190
+ userName: row.user_name,
191
+ accountId: row.account_id,
192
+ active: Number(row.active) === 1,
193
+ createdAt: integer(row.created_at, 'created_at'),
194
+ lastSyncedAt: integer(row.last_synced_at, 'last_synced_at'),
195
+ };
196
+ }
197
+
198
+ function groupFromRow(row: GroupRow): ScimGroupMapping {
199
+ return {
200
+ id: row.id,
201
+ tenantId: row.tenant_id,
202
+ externalId: row.external_id,
203
+ displayName: row.display_name,
204
+ roleKey: row.role_key,
205
+ precedence: integer(row.precedence, 'precedence'),
206
+ memberCount: integer(row.member_count, 'member_count'),
207
+ createdAt: integer(row.created_at, 'created_at'),
208
+ updatedAt: integer(row.updated_at, 'updated_at'),
209
+ };
210
+ }
211
+
212
+ function eventFromRow(row: EventRow): ProvisioningEvent {
213
+ return {
214
+ id: row.id,
215
+ sequence: integer(row.sequence, 'sequence'),
216
+ tenantId: row.tenant_id,
217
+ tokenId: row.token_id,
218
+ operation: row.operation,
219
+ subject: row.subject,
220
+ outcome: row.outcome,
221
+ reason: row.reason,
222
+ occurredAt: integer(row.occurred_at, 'occurred_at'),
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Builds the WHERE clause of a filtered listing. Only the placeholder text is
228
+ * assembled here; every value stays in the parameter channel.
229
+ */
230
+ class Predicates {
231
+ readonly parameters: DatabaseParameter[];
232
+ readonly #clauses: string[] = [];
233
+
234
+ constructor(tenantId: string, tenantColumn = 'tenant_id') {
235
+ this.parameters = [tenantId];
236
+ this.#clauses.push(tenantColumn + ' = $1');
237
+ }
238
+
239
+ add(expression: string, value: DatabaseParameter): void {
240
+ this.parameters.push(value);
241
+ const placeholder = '$' + this.parameters.length;
242
+ this.#clauses.push(expression.replace('?', () => placeholder));
243
+ }
244
+
245
+ get where(): string {
246
+ return this.#clauses.join(' AND ');
247
+ }
248
+
249
+ /** The next placeholder index, for LIMIT and OFFSET. */
250
+ next(value: DatabaseParameter): string {
251
+ this.parameters.push(value);
252
+ return '$' + this.parameters.length;
253
+ }
254
+ }
255
+
256
+ /* The term is matched as a substring, so the three characters LIKE reads as
257
+ syntax are escaped with the backslash PostgreSQL takes as the default escape
258
+ character; `%` typed by a reader searches for a percent sign. */
259
+ function likePattern(term: string): string {
260
+ return '%' + term.replace(/[\\%_]/g, (character) => '\\' + character) + '%';
261
+ }
262
+
263
+ /* A full page may still be the last one; the next page then answers empty. */
264
+ function pageOf<Row extends SortedRow, Record>(
265
+ rows: readonly Row[],
266
+ limit: number,
267
+ record: (row: Row) => Record,
268
+ ): ListPage<Record> {
269
+ const last = rows.at(-1);
270
+ return {
271
+ items: rows.map(record),
272
+ next:
273
+ last && rows.length === limit
274
+ ? {
275
+ key:
276
+ typeof last.sort_key === 'string'
277
+ ? last.sort_key
278
+ : integer(last.sort_key, 'sort_key'),
279
+ id: last.id,
280
+ }
281
+ : null,
282
+ };
283
+ }
284
+
285
+ /** A repository over a platform-owned PostgreSQL handle. */
286
+ export class DatabaseDirectoryRepository implements DirectoryRepository {
287
+ constructor(private readonly database: DatabaseHandle) {}
288
+
289
+ #read<T>(
290
+ tenantId: string,
291
+ run: (transaction: DatabaseTransaction) => Promise<T>,
292
+ ): Promise<T> {
293
+ return this.database.transaction(run, { access: 'read', tenantId });
294
+ }
295
+
296
+ #write<T>(
297
+ tenantId: string,
298
+ run: (transaction: DatabaseTransaction) => Promise<T>,
299
+ ): Promise<T> {
300
+ return this.database.transaction(run, { access: 'write', tenantId });
301
+ }
302
+
303
+ /* The unique index (tenant_id, lower(label)) carries the order, so a page
304
+ resumes through it whichever direction the screen asked for. */
305
+ async listTokenPage(
306
+ tenantId: string,
307
+ query: TokenListQuery,
308
+ ): Promise<ListPage<ScimToken>> {
309
+ const predicates = new Predicates(tenantId);
310
+ if (query.status !== undefined) predicates.add('status = ?', query.status);
311
+ if (query.search !== undefined)
312
+ predicates.add('label ILIKE ?', likePattern(query.search));
313
+ const rows = await this.#page<TokenRow & SortedRow>(
314
+ tenantId,
315
+ 'SELECT ' +
316
+ TOKEN_COLUMNS +
317
+ ', lower(label) AS sort_key FROM directory_scim_tokens',
318
+ predicates,
319
+ query,
320
+ );
321
+ return pageOf(rows, query.limit, (row) => withoutSecret(tokenFromRow(row)));
322
+ }
323
+
324
+ /**
325
+ * One keyset page ordered by `sort_key`, then `id`, both in `direction`.
326
+ * The sort value is projected once in a derived table, so the keyset
327
+ * predicate and the ORDER BY name one plain column while the planner still
328
+ * sees the expression the index was built on.
329
+ */
330
+ async #page<Row extends SortedRow>(
331
+ tenantId: string,
332
+ select: string,
333
+ predicates: Predicates,
334
+ query: {
335
+ readonly direction: ListDirection;
336
+ readonly limit: number;
337
+ readonly after?: ListPosition | undefined;
338
+ },
339
+ ): Promise<readonly Row[]> {
340
+ let where = 'TRUE';
341
+ if (query.after) {
342
+ const keyset = keysetWhere(
343
+ ['sort_key', 'id'],
344
+ [query.after.key, query.after.id],
345
+ {
346
+ direction: query.direction,
347
+ parameterOffset: predicates.parameters.length,
348
+ },
349
+ );
350
+ where = keyset.text;
351
+ predicates.parameters.push(...keyset.parameters);
352
+ }
353
+ const limit = predicates.next(query.limit);
354
+ const order = query.direction === 'desc' ? 'DESC' : 'ASC';
355
+ const result = await this.#read(tenantId, (transaction) =>
356
+ transaction.query<Row>({
357
+ text:
358
+ `SELECT * FROM (${select} WHERE ${predicates.where}) AS page WHERE ` +
359
+ where +
360
+ ` ORDER BY sort_key ${order}, id ${order} LIMIT ` +
361
+ limit,
362
+ parameters: predicates.parameters,
363
+ }),
364
+ );
365
+ return result.rows;
366
+ }
367
+
368
+ async findTokenById(tenantId: string, id: string): Promise<ScimToken | null> {
369
+ const record = await this.#findToken(tenantId, 'id = $2', id);
370
+ return record === null ? null : withoutSecret(record);
371
+ }
372
+
373
+ findTokenByFingerprint(
374
+ tenantId: string,
375
+ fingerprint: string,
376
+ ): Promise<ScimTokenSecret | null> {
377
+ return this.#findToken(tenantId, 'token_fingerprint = $2', fingerprint);
378
+ }
379
+
380
+ async #findToken(
381
+ tenantId: string,
382
+ predicate: string,
383
+ value: string,
384
+ ): Promise<ScimTokenSecret | null> {
385
+ const result = await this.#read(tenantId, (transaction) =>
386
+ transaction.query<TokenRow>({
387
+ text:
388
+ 'SELECT ' +
389
+ TOKEN_COLUMNS +
390
+ ' FROM directory_scim_tokens WHERE tenant_id = $1 AND ' +
391
+ predicate,
392
+ parameters: [tenantId, value],
393
+ }),
394
+ );
395
+ const row = result.rows[0];
396
+ return row ? tokenFromRow(row) : null;
397
+ }
398
+
399
+ async insertToken(record: ScimTokenSecret): Promise<void> {
400
+ await translatingUniqueViolation(() =>
401
+ this.#write(record.tenantId, (transaction) =>
402
+ transaction.execute({
403
+ text: `INSERT INTO directory_scim_tokens
404
+ (id, tenant_id, label, token_fingerprint, token_hash, status,
405
+ created_by, created_at, last_used_at, expires_at, revoked_at)
406
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NULL, $9, NULL)`,
407
+ parameters: [
408
+ record.id,
409
+ record.tenantId,
410
+ record.label,
411
+ record.tokenFingerprint,
412
+ record.tokenHash,
413
+ record.status,
414
+ record.createdBy,
415
+ record.createdAt,
416
+ record.expiresAt,
417
+ ],
418
+ }),
419
+ ),
420
+ );
421
+ }
422
+
423
+ async replaceTokenSecret(input: {
424
+ readonly tenantId: string;
425
+ readonly id: string;
426
+ readonly tokenFingerprint: string;
427
+ readonly tokenHash: string;
428
+ readonly expiresAt: number | null;
429
+ }): Promise<boolean> {
430
+ const result = await this.#write(input.tenantId, (transaction) =>
431
+ transaction.execute({
432
+ /* Only an active row: rotation replaces a working credential and
433
+ never puts one back on a token an owner revoked. */
434
+ text: `UPDATE directory_scim_tokens
435
+ SET token_fingerprint = $3, token_hash = $4, expires_at = $5,
436
+ last_used_at = NULL
437
+ WHERE tenant_id = $1 AND id = $2 AND status = 'active'`,
438
+ parameters: [
439
+ input.tenantId,
440
+ input.id,
441
+ input.tokenFingerprint,
442
+ input.tokenHash,
443
+ input.expiresAt,
444
+ ],
445
+ }),
446
+ );
447
+ return result.affectedRows > 0;
448
+ }
449
+
450
+ async revokeToken(
451
+ tenantId: string,
452
+ id: string,
453
+ revokedAt: number,
454
+ ): Promise<boolean> {
455
+ const result = await this.#write(tenantId, (transaction) =>
456
+ transaction.execute({
457
+ text: `UPDATE directory_scim_tokens
458
+ SET status = 'revoked', revoked_at = $3
459
+ WHERE tenant_id = $1 AND id = $2 AND status = 'active'`,
460
+ parameters: [tenantId, id, revokedAt],
461
+ }),
462
+ );
463
+ return result.affectedRows > 0;
464
+ }
465
+
466
+ async touchToken(
467
+ tenantId: string,
468
+ id: string,
469
+ usedAt: number,
470
+ ): Promise<void> {
471
+ await this.#write(tenantId, (transaction) =>
472
+ transaction.execute({
473
+ text: `UPDATE directory_scim_tokens SET last_used_at = $3
474
+ WHERE tenant_id = $1 AND id = $2`,
475
+ parameters: [tenantId, id, usedAt],
476
+ }),
477
+ );
478
+ }
479
+
480
+ async listUsers(
481
+ tenantId: string,
482
+ filter: ScimUserFilter,
483
+ page: ScimPageRequest,
484
+ ): Promise<ScimSlice<ScimUserMapping>> {
485
+ const predicates = new Predicates(tenantId);
486
+ if (filter.userName !== undefined)
487
+ predicates.add('user_name = ?', filter.userName);
488
+ if (filter.externalId !== undefined)
489
+ predicates.add('external_id = ?', filter.externalId);
490
+ if (filter.id !== undefined) predicates.add('id = ?', filter.id);
491
+ const where = predicates.where;
492
+ const limit = predicates.next(page.count);
493
+ const offset = predicates.next(page.startIndex - 1);
494
+ return this.#read(tenantId, async (transaction) => {
495
+ const total = await transaction.query<CountRow>({
496
+ text:
497
+ 'SELECT count(*) AS total FROM directory_scim_users WHERE ' + where,
498
+ parameters: predicates.parameters.slice(0, -2),
499
+ });
500
+ const rows = await transaction.query<UserRow>({
501
+ text:
502
+ 'SELECT ' +
503
+ USER_COLUMNS +
504
+ ' FROM directory_scim_users WHERE ' +
505
+ where +
506
+ ' ORDER BY user_name, id LIMIT ' +
507
+ limit +
508
+ ' OFFSET ' +
509
+ offset,
510
+ parameters: predicates.parameters,
511
+ });
512
+ return {
513
+ records: rows.rows.map(userFromRow),
514
+ totalResults: integer(total.rows[0]?.total ?? 0, 'total'),
515
+ };
516
+ });
517
+ }
518
+
519
+ findUserById(tenantId: string, id: string): Promise<ScimUserMapping | null> {
520
+ return this.#findUser(tenantId, 'id = $2', id);
521
+ }
522
+
523
+ findUserByUserName(
524
+ tenantId: string,
525
+ userName: string,
526
+ ): Promise<ScimUserMapping | null> {
527
+ return this.#findUser(tenantId, 'user_name = $2', userName);
528
+ }
529
+
530
+ async findUsersByIds(
531
+ tenantId: string,
532
+ ids: readonly string[],
533
+ ): Promise<readonly ScimUserMapping[]> {
534
+ const unique = [...new Set(ids)];
535
+ if (unique.length === 0) return [];
536
+ /* Only the placeholder text is generated; every id stays a bound value. */
537
+ const placeholders = unique.map((_, index) => '$' + (index + 2)).join(', ');
538
+ const result = await this.#read(tenantId, (transaction) =>
539
+ transaction.query<UserRow>({
540
+ text:
541
+ 'SELECT ' +
542
+ USER_COLUMNS +
543
+ ' FROM directory_scim_users WHERE tenant_id = $1 AND id IN (' +
544
+ placeholders +
545
+ ')',
546
+ parameters: [tenantId, ...unique],
547
+ }),
548
+ );
549
+ return result.rows.map(userFromRow);
550
+ }
551
+
552
+ async #findUser(
553
+ tenantId: string,
554
+ predicate: string,
555
+ value: string,
556
+ ): Promise<ScimUserMapping | null> {
557
+ const result = await this.#read(tenantId, (transaction) =>
558
+ transaction.query<UserRow>({
559
+ text:
560
+ 'SELECT ' +
561
+ USER_COLUMNS +
562
+ ' FROM directory_scim_users WHERE tenant_id = $1 AND ' +
563
+ predicate,
564
+ parameters: [tenantId, value],
565
+ }),
566
+ );
567
+ const row = result.rows[0];
568
+ return row ? userFromRow(row) : null;
569
+ }
570
+
571
+ async insertUser(record: ScimUserMapping): Promise<void> {
572
+ await translatingUniqueViolation(() =>
573
+ this.#write(record.tenantId, (transaction) =>
574
+ transaction.execute({
575
+ text: `INSERT INTO directory_scim_users
576
+ (id, tenant_id, external_id, user_name, account_id, active,
577
+ created_at, last_synced_at)
578
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
579
+ parameters: [
580
+ record.id,
581
+ record.tenantId,
582
+ record.externalId,
583
+ record.userName,
584
+ record.accountId,
585
+ record.active ? 1 : 0,
586
+ record.createdAt,
587
+ record.lastSyncedAt,
588
+ ],
589
+ }),
590
+ ),
591
+ );
592
+ }
593
+
594
+ async updateUser(input: {
595
+ readonly tenantId: string;
596
+ readonly id: string;
597
+ readonly externalId: string | null;
598
+ readonly active: boolean;
599
+ readonly lastSyncedAt: number;
600
+ }): Promise<void> {
601
+ await this.#write(input.tenantId, (transaction) =>
602
+ transaction.execute({
603
+ text: `UPDATE directory_scim_users
604
+ SET external_id = $3, active = $4, last_synced_at = $5
605
+ WHERE tenant_id = $1 AND id = $2`,
606
+ parameters: [
607
+ input.tenantId,
608
+ input.id,
609
+ input.externalId,
610
+ input.active ? 1 : 0,
611
+ input.lastSyncedAt,
612
+ ],
613
+ }),
614
+ );
615
+ }
616
+
617
+ async listGroups(
618
+ tenantId: string,
619
+ filter: ScimGroupFilter,
620
+ page: ScimPageRequest,
621
+ ): Promise<ScimSlice<ScimGroupMapping>> {
622
+ const predicates = new Predicates(tenantId, 'g.tenant_id');
623
+ if (filter.displayName !== undefined)
624
+ predicates.add('lower(g.display_name) = lower(?)', filter.displayName);
625
+ if (filter.externalId !== undefined)
626
+ predicates.add('g.external_id = ?', filter.externalId);
627
+ if (filter.id !== undefined) predicates.add('g.id = ?', filter.id);
628
+ const where = predicates.where;
629
+ const limit = predicates.next(page.count);
630
+ const offset = predicates.next(page.startIndex - 1);
631
+ return this.#read(tenantId, async (transaction) => {
632
+ const total = await transaction.query<CountRow>({
633
+ text:
634
+ 'SELECT count(*) AS total FROM directory_scim_groups AS g WHERE ' +
635
+ where,
636
+ parameters: predicates.parameters.slice(0, -2),
637
+ });
638
+ const rows = await transaction.query<GroupRow>({
639
+ text:
640
+ 'SELECT ' +
641
+ GROUP_COLUMNS +
642
+ ' FROM directory_scim_groups AS g WHERE ' +
643
+ where +
644
+ ' ORDER BY g.precedence, lower(g.display_name), g.id LIMIT ' +
645
+ limit +
646
+ ' OFFSET ' +
647
+ offset,
648
+ parameters: predicates.parameters,
649
+ });
650
+ return {
651
+ records: rows.rows.map(groupFromRow),
652
+ totalResults: integer(total.rows[0]?.total ?? 0, 'total'),
653
+ };
654
+ });
655
+ }
656
+
657
+ /* `precedence` pages through (tenant_id, precedence, id) and `displayName`
658
+ through the unique (tenant_id, lower(display_name)); a tie on precedence
659
+ is broken by id, which is what that index carries. */
660
+ async listGroupPage(
661
+ tenantId: string,
662
+ query: GroupListQuery,
663
+ ): Promise<ListPage<ScimGroupMapping>> {
664
+ const predicates = new Predicates(tenantId, 'g.tenant_id');
665
+ if (query.search !== undefined)
666
+ predicates.add('g.display_name ILIKE ?', likePattern(query.search));
667
+ const rows = await this.#page<GroupRow & SortedRow>(
668
+ tenantId,
669
+ 'SELECT ' +
670
+ GROUP_COLUMNS +
671
+ (query.sort === 'precedence'
672
+ ? ', g.precedence AS sort_key'
673
+ : ', lower(g.display_name) AS sort_key') +
674
+ ' FROM directory_scim_groups AS g',
675
+ predicates,
676
+ query,
677
+ );
678
+ return pageOf(rows, query.limit, groupFromRow);
679
+ }
680
+
681
+ findGroupById(
682
+ tenantId: string,
683
+ id: string,
684
+ ): Promise<ScimGroupMapping | null> {
685
+ return this.#findGroup(tenantId, 'g.id = $2', id);
686
+ }
687
+
688
+ async #findGroup(
689
+ tenantId: string,
690
+ predicate: string,
691
+ value: string,
692
+ ): Promise<ScimGroupMapping | null> {
693
+ const result = await this.#read(tenantId, (transaction) =>
694
+ transaction.query<GroupRow>({
695
+ text:
696
+ 'SELECT ' +
697
+ GROUP_COLUMNS +
698
+ ' FROM directory_scim_groups AS g WHERE g.tenant_id = $1 AND ' +
699
+ predicate,
700
+ parameters: [tenantId, value],
701
+ }),
702
+ );
703
+ const row = result.rows[0];
704
+ return row ? groupFromRow(row) : null;
705
+ }
706
+
707
+ async insertGroup(
708
+ record: Omit<ScimGroupMapping, 'memberCount'>,
709
+ ): Promise<void> {
710
+ await translatingUniqueViolation(() =>
711
+ this.#write(record.tenantId, (transaction) =>
712
+ transaction.execute({
713
+ text: `INSERT INTO directory_scim_groups
714
+ (id, tenant_id, external_id, display_name, role_key, precedence,
715
+ created_at, updated_at)
716
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
717
+ parameters: [
718
+ record.id,
719
+ record.tenantId,
720
+ record.externalId,
721
+ record.displayName,
722
+ record.roleKey,
723
+ record.precedence,
724
+ record.createdAt,
725
+ record.updatedAt,
726
+ ],
727
+ }),
728
+ ),
729
+ );
730
+ }
731
+
732
+ async updateGroup(input: {
733
+ readonly tenantId: string;
734
+ readonly id: string;
735
+ readonly externalId: string | null;
736
+ readonly displayName: string;
737
+ readonly roleKey: string | null;
738
+ readonly precedence: number;
739
+ readonly updatedAt: number;
740
+ }): Promise<void> {
741
+ await translatingUniqueViolation(() =>
742
+ this.#write(input.tenantId, (transaction) =>
743
+ transaction.execute({
744
+ text: `UPDATE directory_scim_groups
745
+ SET external_id = $3, display_name = $4, role_key = $5,
746
+ precedence = $6, updated_at = $7
747
+ WHERE tenant_id = $1 AND id = $2`,
748
+ parameters: [
749
+ input.tenantId,
750
+ input.id,
751
+ input.externalId,
752
+ input.displayName,
753
+ input.roleKey,
754
+ input.precedence,
755
+ input.updatedAt,
756
+ ],
757
+ }),
758
+ ),
759
+ );
760
+ }
761
+
762
+ async deleteGroup(tenantId: string, id: string): Promise<boolean> {
763
+ const result = await this.#write(tenantId, (transaction) =>
764
+ transaction.execute({
765
+ text: 'DELETE FROM directory_scim_groups WHERE tenant_id = $1 AND id = $2',
766
+ parameters: [tenantId, id],
767
+ }),
768
+ );
769
+ return result.affectedRows > 0;
770
+ }
771
+
772
+ async listGroupMemberIds(
773
+ tenantId: string,
774
+ groupId: string,
775
+ ): Promise<readonly string[]> {
776
+ const result = await this.#read(tenantId, (transaction) =>
777
+ transaction.query<{ user_id: string }>({
778
+ text: `SELECT user_id FROM directory_scim_group_members
779
+ WHERE tenant_id = $1 AND group_id = $2
780
+ ORDER BY added_at, user_id`,
781
+ parameters: [tenantId, groupId],
782
+ }),
783
+ );
784
+ return result.rows.map((row) => row.user_id);
785
+ }
786
+
787
+ async listMembersOfGroups(
788
+ tenantId: string,
789
+ groupIds: readonly string[],
790
+ perGroupLimit: number,
791
+ ): Promise<readonly GroupMemberRow[]> {
792
+ const ids = [...new Set(groupIds)];
793
+ if (ids.length === 0 || perGroupLimit < 1) return [];
794
+ /* Only the placeholder text is generated; every id stays a bound value. */
795
+ const placeholders = ids.map((_, index) => '$' + (index + 3)).join(', ');
796
+ /* The window keeps one group's members from filling a whole page: a group
797
+ with more members than the bound is truncated, never streamed. */
798
+ const result = await this.#read(tenantId, (transaction) =>
799
+ transaction.query<{
800
+ group_id: string;
801
+ user_id: string;
802
+ user_name: string;
803
+ }>({
804
+ text:
805
+ `SELECT group_id, user_id, user_name FROM (
806
+ SELECT m.group_id, m.user_id, u.user_name,
807
+ row_number() OVER (
808
+ PARTITION BY m.group_id ORDER BY u.user_name, m.user_id
809
+ ) AS position
810
+ FROM directory_scim_group_members AS m
811
+ JOIN directory_scim_users AS u
812
+ ON u.id = m.user_id AND u.tenant_id = m.tenant_id
813
+ WHERE m.tenant_id = $1 AND m.group_id IN (` +
814
+ placeholders +
815
+ `)
816
+ ) AS page
817
+ WHERE position <= $2
818
+ ORDER BY group_id, user_name`,
819
+ parameters: [tenantId, perGroupLimit, ...ids],
820
+ }),
821
+ );
822
+ return result.rows.map((row) => ({
823
+ groupId: row.group_id,
824
+ userId: row.user_id,
825
+ userName: row.user_name,
826
+ }));
827
+ }
828
+
829
+ applyGroupMembers(input: {
830
+ readonly tenantId: string;
831
+ readonly groupId: string;
832
+ readonly steps: readonly GroupMemberStep[];
833
+ readonly now: number;
834
+ }): Promise<GroupMemberChange> {
835
+ /* The membership is read and rewritten in one transaction, so a
836
+ concurrent change cannot make the diff act on a state that already
837
+ moved, and the steps keep the order the provider sent them in. */
838
+ return this.#write(input.tenantId, async (transaction) => {
839
+ const current = await transaction.query<{ user_id: string }>({
840
+ text: `SELECT user_id FROM directory_scim_group_members
841
+ WHERE tenant_id = $1 AND group_id = $2`,
842
+ parameters: [input.tenantId, input.groupId],
843
+ });
844
+ const before = new Set(current.rows.map((row) => row.user_id));
845
+ const after = new Set(before);
846
+ for (const step of input.steps) {
847
+ if (step.kind === 'set') {
848
+ after.clear();
849
+ for (const member of step.members) after.add(member);
850
+ } else if (step.kind === 'add') {
851
+ for (const member of step.members) after.add(member);
852
+ } else {
853
+ for (const member of step.members) after.delete(member);
854
+ }
855
+ }
856
+ const added = [...after].filter((member) => !before.has(member));
857
+ const removed = [...before].filter((member) => !after.has(member));
858
+ for (const userId of removed) {
859
+ await transaction.execute({
860
+ text: `DELETE FROM directory_scim_group_members
861
+ WHERE tenant_id = $1 AND group_id = $2 AND user_id = $3`,
862
+ parameters: [input.tenantId, input.groupId, userId],
863
+ });
864
+ }
865
+ for (const userId of added) {
866
+ await transaction.execute({
867
+ text: `INSERT INTO directory_scim_group_members
868
+ (tenant_id, group_id, user_id, added_at)
869
+ VALUES ($1, $2, $3, $4)`,
870
+ parameters: [input.tenantId, input.groupId, userId, input.now],
871
+ });
872
+ }
873
+ return { added, removed };
874
+ });
875
+ }
876
+
877
+ resolvedRolesForGroup(
878
+ tenantId: string,
879
+ groupId: string,
880
+ ): Promise<readonly ResolvedGroupMember[]> {
881
+ return this.#resolvedRoles(
882
+ tenantId,
883
+ `mapped_user.id IN (SELECT current_member.user_id
884
+ FROM directory_scim_group_members AS current_member
885
+ WHERE current_member.tenant_id = $1 AND current_member.group_id = $2)`,
886
+ [groupId],
887
+ );
888
+ }
889
+
890
+ resolvedRolesForUsers(
891
+ tenantId: string,
892
+ userIds: readonly string[],
893
+ ): Promise<readonly ResolvedGroupMember[]> {
894
+ const unique = [...new Set(userIds)];
895
+ if (unique.length === 0) return Promise.resolve([]);
896
+ /* Only the placeholder text is generated; every id stays a bound value. */
897
+ const placeholders = unique.map((_, index) => '$' + (index + 2)).join(', ');
898
+ return this.#resolvedRoles(
899
+ tenantId,
900
+ 'mapped_user.id IN (' + placeholders + ')',
901
+ unique,
902
+ );
903
+ }
904
+
905
+ /**
906
+ * The winning role of every named user in one pass: each user appears once,
907
+ * carrying the mapped role of lowest precedence among the groups they belong
908
+ * to, and a null role when no mapped group applies. The membership table is
909
+ * joined from the user rather than walked from it, so a user whose last
910
+ * group was just removed still answers, with nothing mapped.
911
+ */
912
+ #resolvedRoles(
913
+ tenantId: string,
914
+ selection: string,
915
+ parameters: readonly DatabaseParameter[],
916
+ ): Promise<readonly ResolvedGroupMember[]> {
917
+ return this.#read(tenantId, async (transaction) => {
918
+ const result = await transaction.query<ResolvedRoleRow>({
919
+ text: `SELECT DISTINCT ON (mapped_user.id)
920
+ mapped_user.id AS user_id, mapped_user.account_id,
921
+ mapped_group.role_key
922
+ FROM directory_scim_users AS mapped_user
923
+ LEFT JOIN directory_scim_group_members AS member
924
+ ON member.user_id = mapped_user.id
925
+ AND member.tenant_id = mapped_user.tenant_id
926
+ LEFT JOIN directory_scim_groups AS mapped_group
927
+ ON mapped_group.id = member.group_id
928
+ AND mapped_group.tenant_id = member.tenant_id
929
+ AND mapped_group.role_key IS NOT NULL
930
+ WHERE mapped_user.tenant_id = $1 AND ${selection}
931
+ ORDER BY mapped_user.id, mapped_group.precedence NULLS LAST,
932
+ mapped_group.id`,
933
+ parameters: [tenantId, ...parameters],
934
+ });
935
+ return result.rows.map((row) => ({
936
+ userId: row.user_id,
937
+ accountId: row.account_id,
938
+ roleKey: row.role_key,
939
+ }));
940
+ });
941
+ }
942
+
943
+ appendEvent(event: Omit<ProvisioningEvent, 'sequence'>): Promise<void> {
944
+ return this.appendEvents([event]);
945
+ }
946
+
947
+ /* One statement per batch: a membership change touching a whole group would
948
+ otherwise pay a transaction per member it recorded. */
949
+ async appendEvents(
950
+ events: readonly Omit<ProvisioningEvent, 'sequence'>[],
951
+ ): Promise<void> {
952
+ const first = events[0];
953
+ if (!first) return;
954
+ /* The statement binds one tenant id for every row it writes, so a batch
955
+ spanning two workspaces would file one of them under the other. */
956
+ if (events.some((event) => event.tenantId !== first.tenantId)) {
957
+ throw new Error('A provisioning event batch covers one workspace.');
958
+ }
959
+ for (let start = 0; start < events.length; start += EVENT_INSERT_BATCH) {
960
+ const batch = events.slice(start, start + EVENT_INSERT_BATCH);
961
+ const parameters: DatabaseParameter[] = [first.tenantId];
962
+ const rows = batch.map((event) => {
963
+ const placeholders = [
964
+ event.id,
965
+ event.tokenId,
966
+ event.operation,
967
+ event.subject,
968
+ event.outcome,
969
+ event.reason,
970
+ event.occurredAt,
971
+ ].map((value) => {
972
+ parameters.push(value);
973
+ return '$' + parameters.length;
974
+ });
975
+ return `(${placeholders[0]}, $1, ${placeholders.slice(1).join(', ')})`;
976
+ });
977
+ await this.#write(first.tenantId, (transaction) =>
978
+ transaction.execute({
979
+ text: `INSERT INTO directory_provisioning_events
980
+ (id, tenant_id, token_id, operation, subject, outcome, reason, occurred_at)
981
+ VALUES ${rows.join(', ')}`,
982
+ parameters,
983
+ }),
984
+ );
985
+ }
986
+ }
987
+
988
+ async listEvents(
989
+ tenantId: string,
990
+ query: ProvisioningEventQuery,
991
+ ): Promise<readonly ProvisioningEvent[]> {
992
+ const predicates = new Predicates(tenantId);
993
+ if (query.operation !== undefined)
994
+ predicates.add('operation = ?', query.operation);
995
+ if (query.outcome !== undefined)
996
+ predicates.add('outcome = ?', query.outcome);
997
+ let where = predicates.where;
998
+ if (query.cursor) {
999
+ /* Keyset paging over the same order the index carries, so a page never
1000
+ re-reads the rows before it. */
1001
+ const keyset = keysetWhere(
1002
+ ['occurred_at', 'sequence'],
1003
+ [query.cursor.occurredAt, query.cursor.sequence],
1004
+ { direction: 'desc', parameterOffset: predicates.parameters.length },
1005
+ );
1006
+ where += ' AND ' + keyset.text;
1007
+ predicates.parameters.push(...keyset.parameters);
1008
+ }
1009
+ const limit = predicates.next(query.limit);
1010
+ const result = await this.#read(tenantId, (transaction) =>
1011
+ transaction.query<EventRow>({
1012
+ text:
1013
+ `SELECT id, sequence, tenant_id, token_id, operation, subject,
1014
+ outcome, reason, occurred_at
1015
+ FROM directory_provisioning_events WHERE ` +
1016
+ where +
1017
+ ' ORDER BY occurred_at DESC, sequence DESC LIMIT ' +
1018
+ limit,
1019
+ parameters: predicates.parameters,
1020
+ }),
1021
+ );
1022
+ return result.rows.map(eventFromRow);
1023
+ }
1024
+
1025
+ /* The index is (tenant_id, occurred_at DESC, sequence DESC), so the subquery
1026
+ picks the batch through it and the delete removes exactly those rows. */
1027
+ async sweepEvents(
1028
+ tenantId: string,
1029
+ before: number,
1030
+ limit: number,
1031
+ ): Promise<{ readonly removed: number }> {
1032
+ const removed = await this.#write(tenantId, (transaction) =>
1033
+ transaction.execute({
1034
+ text: `DELETE FROM directory_provisioning_events WHERE id IN (
1035
+ SELECT id FROM directory_provisioning_events
1036
+ WHERE tenant_id = $1 AND occurred_at < $2
1037
+ ORDER BY occurred_at, sequence LIMIT $3)`,
1038
+ parameters: [tenantId, before, limit],
1039
+ }),
1040
+ );
1041
+ return { removed: removed.affectedRows };
1042
+ }
1043
+
1044
+ async exportEventsPage(
1045
+ tenantId: string,
1046
+ afterSequence: number,
1047
+ limit: number,
1048
+ ): Promise<readonly ProvisioningEvent[]> {
1049
+ const result = await this.#read(tenantId, (transaction) =>
1050
+ transaction.query<EventRow>({
1051
+ text: `SELECT id, sequence, tenant_id, token_id, operation, subject,
1052
+ outcome, reason, occurred_at
1053
+ FROM directory_provisioning_events
1054
+ WHERE tenant_id = $1 AND sequence > $2
1055
+ ORDER BY sequence LIMIT $3`,
1056
+ parameters: [tenantId, afterSequence, limit],
1057
+ }),
1058
+ );
1059
+ return result.rows.map(eventFromRow);
1060
+ }
1061
+ }
1062
+
1063
+ export async function migrateDirectoryDatabase(
1064
+ database: DatabaseHandle,
1065
+ ): Promise<void> {
1066
+ await runDatabaseMigrations(database, 'directory.core', databaseMigrations);
1067
+ }