@flowdular/sdk 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (930) 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 +130 -3
  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/cli-extension/SKILL.md +1 -1
  30. package/.ai/skills/deploy-operate/SKILL.md +119 -0
  31. package/.ai/skills/module-new/SKILL.md +29 -3
  32. package/.ai/skills/module-update/SKILL.md +9 -3
  33. package/.ai/skills/perf-audit/SKILL.md +0 -1
  34. package/.ai/skills/release-eject-pr/SKILL.md +0 -1
  35. package/.ai/skills/spec-interview/SKILL.md +120 -0
  36. package/.ai/skills/test-hardening/SKILL.md +1 -0
  37. package/.ai/skills/ux-design/SKILL.md +34 -3
  38. package/.ai/skills/variables/SKILL.md +0 -2
  39. package/.ai/skills/workflow-development/SKILL.md +0 -1
  40. package/AGENTS.md +4 -0
  41. package/docs/agent-contract.md +2 -2
  42. package/docs/design-system.md +185 -31
  43. package/modules/access/migrations/0001_access_core.down.sql +1 -0
  44. package/modules/access/migrations/0001_access_core.up.sql +28 -0
  45. package/modules/access/migrations/README.md +3 -0
  46. package/modules/access/module.json +33 -0
  47. package/modules/access/package.json +43 -0
  48. package/modules/access/spec/module.yaml +225 -0
  49. package/modules/access/src/acl/permissions.ts +6 -0
  50. package/modules/access/src/api/endpoints.ts +244 -0
  51. package/modules/access/src/client/ActivityView.tsrx +245 -0
  52. package/modules/access/src/client/AttestationsView.tsrx +176 -0
  53. package/modules/access/src/client/ReviewView.tsrx +586 -0
  54. package/modules/access/src/client/api.ts +132 -0
  55. package/modules/access/src/client/contribution.tsrx +41 -0
  56. package/modules/access/src/client/index.ts +41 -0
  57. package/modules/access/src/client/navigation.ts +57 -0
  58. package/modules/access/src/client/presentation.ts +69 -0
  59. package/modules/access/src/client/state.ts +72 -0
  60. package/modules/access/src/domain/types.ts +189 -0
  61. package/modules/access/src/index.ts +38 -0
  62. package/modules/access/src/platform.ts +60 -0
  63. package/modules/access/src/server/index.ts +42 -0
  64. package/modules/access/src/server/runtime.ts +105 -0
  65. package/modules/access/src/services/access-service.ts +298 -0
  66. package/modules/access/src/services/audit-window.ts +78 -0
  67. package/modules/access/src/services/auth-directory.ts +72 -0
  68. package/modules/access/src/services/changes.ts +91 -0
  69. package/modules/access/src/services/data-classes.ts +28 -0
  70. package/modules/access/src/services/database-repository.ts +185 -0
  71. package/modules/access/src/services/directory.ts +102 -0
  72. package/modules/access/src/services/index.ts +33 -0
  73. package/modules/access/src/services/list-exports.ts +150 -0
  74. package/modules/access/src/services/migration.ts +54 -0
  75. package/modules/access/src/services/repository.ts +30 -0
  76. package/modules/access/src/services/review.ts +178 -0
  77. package/modules/access/translations/en.json +155 -0
  78. package/modules/access/translations/pl.json +155 -0
  79. package/modules/agents/migrations/0022_credential_rotation_inventory.down.sql +2 -0
  80. package/modules/agents/migrations/0022_credential_rotation_inventory.up.sql +7 -0
  81. package/modules/agents/migrations/0023_agents_retention_indexes.down.sql +2 -0
  82. package/modules/agents/migrations/0023_agents_retention_indexes.up.sql +9 -0
  83. package/modules/agents/migrations/0024_agent_meter_refusals.down.sql +3 -0
  84. package/modules/agents/migrations/0024_agent_meter_refusals.up.sql +22 -0
  85. package/modules/agents/migrations/0025_agent_list_indexes.down.sql +3 -0
  86. package/modules/agents/migrations/0025_agent_list_indexes.up.sql +11 -0
  87. package/modules/agents/module.json +30 -4
  88. package/modules/agents/package.json +2 -1
  89. package/modules/agents/spec/module.yaml +63 -5
  90. package/modules/agents/src/api/endpoints.ts +291 -33
  91. package/modules/agents/src/cli/commands.json +11 -0
  92. package/modules/agents/src/cli/index.ts +45 -0
  93. package/modules/agents/src/client/AgentDashboardWidget.tsrx +19 -11
  94. package/modules/agents/src/client/AgentDefinitionList.tsrx +53 -10
  95. package/modules/agents/src/client/AgentPlaygroundView.tsrx +4 -4
  96. package/modules/agents/src/client/AgentProceduresView.tsrx +2 -2
  97. package/modules/agents/src/client/AgentRunsView.tsrx +143 -40
  98. package/modules/agents/src/client/AgentsView.tsrx +109 -33
  99. package/modules/agents/src/client/api.ts +99 -21
  100. package/modules/agents/src/client/state.ts +97 -0
  101. package/modules/agents/src/domain/types.ts +44 -4
  102. package/modules/agents/src/platform.ts +42 -0
  103. package/modules/agents/src/server/action-execution.ts +201 -106
  104. package/modules/agents/src/server/runtime.ts +32 -0
  105. package/modules/agents/src/services/agent-service.ts +158 -30
  106. package/modules/agents/src/services/credential-rotation.ts +185 -0
  107. package/modules/agents/src/services/credential-vault.ts +71 -46
  108. package/modules/agents/src/services/data-classes.ts +176 -0
  109. package/modules/agents/src/services/database-repository.ts +393 -101
  110. package/modules/agents/src/services/metering.ts +111 -0
  111. package/modules/agents/src/services/migration.ts +109 -0
  112. package/modules/agents/src/services/module-agent-preflight.ts +17 -9
  113. package/modules/agents/src/services/notifications.ts +98 -0
  114. package/modules/agents/src/services/provider-repository.ts +9 -18
  115. package/modules/agents/src/services/provider-service.ts +1 -9
  116. package/modules/agents/src/services/reports.ts +107 -0
  117. package/modules/agents/src/services/repository.ts +86 -6
  118. package/modules/agents/src/services/usage-service.ts +2 -0
  119. package/modules/agents/src/services/worker.ts +114 -4
  120. package/modules/agents/src/settings.ts +14 -3
  121. package/modules/agents/translations/en.json +14 -4
  122. package/modules/agents/translations/pl.json +14 -4
  123. package/modules/approvals/migrations/0001_approvals_core.down.sql +3 -0
  124. package/modules/approvals/migrations/0001_approvals_core.up.sql +78 -0
  125. package/modules/approvals/migrations/0002_approvals_expiry_routing_role.down.sql +2 -0
  126. package/modules/approvals/migrations/0002_approvals_expiry_routing_role.up.sql +19 -0
  127. package/modules/approvals/migrations/0003_approvals_retention_indexes.down.sql +6 -0
  128. package/modules/approvals/migrations/0003_approvals_retention_indexes.up.sql +14 -0
  129. package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.down.sql +5 -0
  130. package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.up.sql +12 -0
  131. package/modules/approvals/migrations/0005_approvals_grant_audit.down.sql +4 -0
  132. package/modules/approvals/migrations/0005_approvals_grant_audit.up.sql +20 -0
  133. package/modules/approvals/migrations/README.md +3 -0
  134. package/modules/approvals/module.json +34 -0
  135. package/modules/approvals/package.json +43 -0
  136. package/modules/approvals/spec/module.yaml +327 -0
  137. package/modules/approvals/src/acl/permissions.ts +7 -0
  138. package/modules/approvals/src/api/endpoints.ts +457 -0
  139. package/modules/approvals/src/client/ApprovalRequestDrawer.tsrx +138 -0
  140. package/modules/approvals/src/client/ApprovalsView.tsrx +562 -0
  141. package/modules/approvals/src/client/PendingCountWidget.tsrx +52 -0
  142. package/modules/approvals/src/client/api.ts +203 -0
  143. package/modules/approvals/src/client/approvals.css +51 -0
  144. package/modules/approvals/src/client/contribution.tsrx +44 -0
  145. package/modules/approvals/src/client/index.ts +26 -0
  146. package/modules/approvals/src/client/navigation.ts +39 -0
  147. package/modules/approvals/src/client/pending-count.ts +26 -0
  148. package/modules/approvals/src/client/presentation.ts +52 -0
  149. package/modules/approvals/src/client/state.ts +141 -0
  150. package/modules/approvals/src/domain/capability.ts +107 -0
  151. package/modules/approvals/src/domain/grant.ts +69 -0
  152. package/modules/approvals/src/domain/types.ts +168 -0
  153. package/modules/approvals/src/index.ts +61 -0
  154. package/modules/approvals/src/platform.ts +136 -0
  155. package/modules/approvals/src/server/index.ts +42 -0
  156. package/modules/approvals/src/server/runtime.ts +185 -0
  157. package/modules/approvals/src/services/approvals-service.ts +637 -0
  158. package/modules/approvals/src/services/callbacks.ts +75 -0
  159. package/modules/approvals/src/services/data-classes.ts +167 -0
  160. package/modules/approvals/src/services/database-repository.ts +783 -0
  161. package/modules/approvals/src/services/eligibility.ts +121 -0
  162. package/modules/approvals/src/services/expiry-runner.ts +81 -0
  163. package/modules/approvals/src/services/index.ts +31 -0
  164. package/modules/approvals/src/services/migration.ts +256 -0
  165. package/modules/approvals/src/services/notifications.ts +95 -0
  166. package/modules/approvals/src/services/repository.ts +169 -0
  167. package/modules/approvals/src/services/service-error.ts +70 -0
  168. package/modules/approvals/src/settings.ts +102 -0
  169. package/modules/approvals/translations/en.json +104 -0
  170. package/modules/approvals/translations/pl.json +104 -0
  171. package/modules/audit/migrations/0001_audit_core.down.sql +8 -0
  172. package/modules/audit/migrations/0001_audit_core.up.sql +107 -0
  173. package/modules/audit/migrations/0002_audit_sweep_routing_role.down.sql +2 -0
  174. package/modules/audit/migrations/0002_audit_sweep_routing_role.up.sql +21 -0
  175. package/modules/audit/migrations/0003_audit_export_request.down.sql +12 -0
  176. package/modules/audit/migrations/0003_audit_export_request.up.sql +40 -0
  177. package/modules/audit/migrations/0004_audit_hold_seal_erasure.down.sql +17 -0
  178. package/modules/audit/migrations/0004_audit_hold_seal_erasure.up.sql +192 -0
  179. package/modules/audit/migrations/0005_audit_event_format.down.sql +17 -0
  180. package/modules/audit/migrations/0005_audit_event_format.up.sql +60 -0
  181. package/modules/audit/migrations/0006_audit_own_class_walks.down.sql +3 -0
  182. package/modules/audit/migrations/0006_audit_own_class_walks.up.sql +14 -0
  183. package/modules/audit/migrations/README.md +3 -0
  184. package/modules/audit/module.json +32 -0
  185. package/modules/audit/package.json +49 -0
  186. package/modules/audit/spec/module.yaml +809 -0
  187. package/modules/audit/src/acl/permissions.ts +7 -0
  188. package/modules/audit/src/api/endpoints.ts +315 -0
  189. package/modules/audit/src/cli/commands.json +95 -0
  190. package/modules/audit/src/cli/index.ts +658 -0
  191. package/modules/audit/src/client/DataClassesView.tsrx +420 -0
  192. package/modules/audit/src/client/ExportsView.tsrx +256 -0
  193. package/modules/audit/src/client/HoldsView.tsrx +604 -0
  194. package/modules/audit/src/client/SweepsView.tsrx +251 -0
  195. package/modules/audit/src/client/api.ts +185 -0
  196. package/modules/audit/src/client/contribution.tsrx +52 -0
  197. package/modules/audit/src/client/index.ts +23 -0
  198. package/modules/audit/src/client/navigation.ts +74 -0
  199. package/modules/audit/src/client/presentation.ts +105 -0
  200. package/modules/audit/src/client/state.ts +86 -0
  201. package/modules/audit/src/domain/data-classes.ts +16 -0
  202. package/modules/audit/src/domain/types.ts +339 -0
  203. package/modules/audit/src/index.ts +76 -0
  204. package/modules/audit/src/platform.ts +44 -0
  205. package/modules/audit/src/server/index.ts +177 -0
  206. package/modules/audit/src/server/runtime.ts +303 -0
  207. package/modules/audit/src/services/anchor-key.ts +143 -0
  208. package/modules/audit/src/services/anchor-rotation.ts +147 -0
  209. package/modules/audit/src/services/audit-runners.ts +278 -0
  210. package/modules/audit/src/services/backup-guard.ts +95 -0
  211. package/modules/audit/src/services/database-repository.ts +1946 -0
  212. package/modules/audit/src/services/declared-classes.ts +64 -0
  213. package/modules/audit/src/services/erasure-port.ts +126 -0
  214. package/modules/audit/src/services/erasure-service.ts +770 -0
  215. package/modules/audit/src/services/export-directory.ts +106 -0
  216. package/modules/audit/src/services/export-service.ts +714 -0
  217. package/modules/audit/src/services/hold-service.ts +295 -0
  218. package/modules/audit/src/services/index.ts +17 -0
  219. package/modules/audit/src/services/migration.ts +658 -0
  220. package/modules/audit/src/services/own-classes.ts +318 -0
  221. package/modules/audit/src/services/platform-version.ts +23 -0
  222. package/modules/audit/src/services/repository.ts +495 -0
  223. package/modules/audit/src/services/retention-service.ts +191 -0
  224. package/modules/audit/src/services/seal-service.ts +769 -0
  225. package/modules/audit/src/services/service-error.ts +61 -0
  226. package/modules/audit/src/services/subject-keys.ts +90 -0
  227. package/modules/audit/src/services/sweep-service.ts +298 -0
  228. package/modules/audit/src/services/zip.ts +217 -0
  229. package/modules/audit/src/settings.ts +69 -0
  230. package/modules/audit/translations/en.json +210 -0
  231. package/modules/audit/translations/pl.json +210 -0
  232. package/modules/auth/README.md +77 -29
  233. package/modules/auth/migrations/0016_external_identities.up.sql +16 -0
  234. package/modules/auth/migrations/0017_mfa_key_id.up.sql +8 -0
  235. package/modules/auth/migrations/0018_retire_bundled_module_scopes.up.sql +29 -0
  236. package/modules/auth/migrations/0019_notifications_member_scopes.up.sql +44 -0
  237. package/modules/auth/migrations/0020_identity_providers.up.sql +61 -0
  238. package/modules/auth/migrations/0021_membership_status.up.sql +19 -0
  239. package/modules/auth/migrations/0022_auth_provider_scopes.up.sql +45 -0
  240. package/modules/auth/migrations/0023_enterprise_module_scopes.down.sql +19 -0
  241. package/modules/auth/migrations/0023_enterprise_module_scopes.up.sql +71 -0
  242. package/modules/auth/migrations/0024_approvals_member_scopes.down.sql +22 -0
  243. package/modules/auth/migrations/0024_approvals_member_scopes.up.sql +48 -0
  244. package/modules/auth/migrations/0025_audit_holds_owner_scope.down.sql +21 -0
  245. package/modules/auth/migrations/0025_audit_holds_owner_scope.up.sql +47 -0
  246. package/modules/auth/migrations/0026_auth_export_keyset_indexes.down.sql +6 -0
  247. package/modules/auth/migrations/0026_auth_export_keyset_indexes.up.sql +15 -0
  248. package/modules/auth/migrations/0027_workflow_automation_profile_scopes.down.sql +34 -0
  249. package/modules/auth/migrations/0027_workflow_automation_profile_scopes.up.sql +72 -0
  250. package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.down.sql +6 -0
  251. package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.up.sql +21 -0
  252. package/modules/auth/migrations/0029_reports_exports_access_scopes.down.sql +22 -0
  253. package/modules/auth/migrations/0029_reports_exports_access_scopes.up.sql +56 -0
  254. package/modules/auth/migrations/0030_auth_membership_keyset_index.down.sql +4 -0
  255. package/modules/auth/migrations/0030_auth_membership_keyset_index.up.sql +10 -0
  256. package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.down.sql +4 -0
  257. package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.up.sql +31 -0
  258. package/modules/auth/migrations/0032_audit_actor_service.down.sql +9 -0
  259. package/modules/auth/migrations/0032_audit_actor_service.up.sql +10 -0
  260. package/modules/auth/migrations/0033_auth_account_keyset_indexes.down.sql +5 -0
  261. package/modules/auth/migrations/0033_auth_account_keyset_indexes.up.sql +23 -0
  262. package/modules/auth/module.json +3 -3
  263. package/modules/auth/package.json +2 -1
  264. package/modules/auth/spec/module.yaml +372 -3
  265. package/modules/auth/src/acl/scopes.ts +50 -6
  266. package/modules/auth/src/cli/commands.json +11 -0
  267. package/modules/auth/src/cli/greenfield.ts +2 -2
  268. package/modules/auth/src/cli/index.ts +14 -0
  269. package/modules/auth/src/cli/secrets.ts +114 -0
  270. package/modules/auth/src/client/AuthenticationCore.tsrx +61 -8
  271. package/modules/auth/src/client/api.ts +72 -6
  272. package/modules/auth/src/client/contribution.tsrx +23 -0
  273. package/modules/auth/src/client/index.ts +6 -0
  274. package/modules/auth/src/client/providers/IdentityProviderForm.tsrx +191 -0
  275. package/modules/auth/src/client/providers/IdentityProvidersView.tsrx +358 -0
  276. package/modules/auth/src/client/providers/api.ts +128 -0
  277. package/modules/auth/src/client/providers/provider-columns.tsrx +60 -0
  278. package/modules/auth/src/client/providers/state.ts +35 -0
  279. package/modules/auth/src/client/security/SecurityView.tsrx +9 -1
  280. package/modules/auth/src/client/state.ts +15 -1
  281. package/modules/auth/src/domain/types.ts +55 -0
  282. package/modules/auth/src/index.ts +11 -1
  283. package/modules/auth/src/middleware/authentication.ts +4 -0
  284. package/modules/auth/src/server/endpoints.ts +467 -171
  285. package/modules/auth/src/server/http.ts +3 -2
  286. package/modules/auth/src/server/index.ts +22 -1
  287. package/modules/auth/src/server/membership-endpoints.ts +62 -0
  288. package/modules/auth/src/server/mfa-enforcement.ts +156 -0
  289. package/modules/auth/src/server/oidc.ts +389 -0
  290. package/modules/auth/src/server/provider-endpoints.ts +246 -0
  291. package/modules/auth/src/server/provider-host.ts +77 -0
  292. package/modules/auth/src/server/runtime.ts +231 -55
  293. package/modules/auth/src/services/auth-service-error.ts +7 -2
  294. package/modules/auth/src/services/auth-service.ts +948 -51
  295. package/modules/auth/src/services/data-classes.ts +236 -0
  296. package/modules/auth/src/services/database-repository.ts +853 -71
  297. package/modules/auth/src/services/identity-provider-service.ts +571 -0
  298. package/modules/auth/src/services/mail-delivery.ts +16 -16
  299. package/modules/auth/src/services/mail-message.ts +54 -0
  300. package/modules/auth/src/services/mail-port.ts +24 -0
  301. package/modules/auth/src/services/mail-smtp.ts +85 -0
  302. package/modules/auth/src/services/mfa-rotation.ts +144 -0
  303. package/modules/auth/src/services/migration.ts +810 -0
  304. package/modules/auth/src/services/password.ts +14 -0
  305. package/modules/auth/src/services/provider-secret-rotation.ts +169 -0
  306. package/modules/auth/src/services/provider-secrets.ts +136 -0
  307. package/modules/auth/src/services/repository.ts +321 -5
  308. package/modules/auth/src/services/session-sweep-runner.ts +73 -0
  309. package/modules/auth/src/services/settings-store.ts +10 -83
  310. package/modules/auth/src/services/totp.ts +88 -62
  311. package/modules/auth/src/services/validation.ts +194 -7
  312. package/modules/auth/src/settings.ts +18 -0
  313. package/modules/auth/translations/en.json +84 -1
  314. package/modules/auth/translations/pl.json +84 -1
  315. package/modules/automations/migrations/0005_secret_rotation_inventory.down.sql +1 -0
  316. package/modules/automations/migrations/0005_secret_rotation_inventory.up.sql +6 -0
  317. package/modules/automations/migrations/0006_automations_list_sort_indexes.down.sql +4 -0
  318. package/modules/automations/migrations/0006_automations_list_sort_indexes.up.sql +11 -0
  319. package/modules/automations/module.json +24 -6
  320. package/modules/automations/package.json +4 -1
  321. package/modules/automations/spec/module.yaml +81 -7
  322. package/modules/automations/src/api/endpoints.ts +202 -28
  323. package/modules/automations/src/cli/commands.json +17 -0
  324. package/modules/automations/src/cli/index.ts +114 -0
  325. package/modules/automations/src/client/AutomationScheduleForm.tsrx +77 -19
  326. package/modules/automations/src/client/AutomationSchedulesView.tsrx +144 -40
  327. package/modules/automations/src/client/AutomationTriggersView.tsrx +131 -32
  328. package/modules/automations/src/client/api.ts +62 -19
  329. package/modules/automations/src/client/presentation.ts +20 -2
  330. package/modules/automations/src/client/state.ts +84 -0
  331. package/modules/automations/src/domain/cadence.ts +99 -3
  332. package/modules/automations/src/domain/cron.ts +366 -0
  333. package/modules/automations/src/domain/time-zone.ts +61 -0
  334. package/modules/automations/src/platform.ts +38 -4
  335. package/modules/automations/src/server/index.ts +11 -0
  336. package/modules/automations/src/server/runtime.ts +46 -22
  337. package/modules/{automations-workflows-integration/src/server/adapter.ts → automations/src/server/workflow-target.ts} +13 -14
  338. package/modules/{automations-workflows-integration/src/server/tools.ts → automations/src/server/workflow-tools.ts} +0 -0
  339. package/modules/automations/src/services/data-classes.ts +62 -0
  340. package/modules/automations/src/services/database-repository.ts +152 -38
  341. package/modules/automations/src/services/migration.ts +119 -9
  342. package/modules/automations/src/services/repository.ts +52 -1
  343. package/modules/automations/src/services/schedule-runner.ts +84 -0
  344. package/modules/automations/src/services/schedule-service.ts +152 -26
  345. package/modules/automations/src/services/secret-rotation.ts +176 -0
  346. package/modules/automations/src/services/secret-vault.ts +68 -41
  347. package/modules/automations/src/services/trigger-service.ts +13 -10
  348. package/modules/automations/translations/en.json +15 -2
  349. package/modules/automations/translations/pl.json +15 -2
  350. package/modules/connectors/migrations/0001_connectors_core.down.sql +3 -0
  351. package/modules/connectors/migrations/0001_connectors_core.up.sql +72 -0
  352. package/modules/connectors/migrations/0002_connectors_call_keys.down.sql +3 -0
  353. package/modules/connectors/migrations/0002_connectors_call_keys.up.sql +18 -0
  354. package/modules/connectors/migrations/0003_connectors_rotation_inventory.down.sql +2 -0
  355. package/modules/connectors/migrations/0003_connectors_rotation_inventory.up.sql +19 -0
  356. package/modules/connectors/migrations/README.md +3 -0
  357. package/modules/connectors/module.json +32 -0
  358. package/modules/{automations-workflows-integration → connectors}/package.json +28 -26
  359. package/modules/connectors/spec/module.yaml +448 -0
  360. package/modules/connectors/src/acl/permissions.ts +6 -0
  361. package/modules/connectors/src/agent/tools.ts +180 -0
  362. package/modules/connectors/src/api/endpoints.ts +572 -0
  363. package/modules/connectors/src/cli/commands.json +17 -0
  364. package/modules/connectors/src/cli/index.ts +126 -0
  365. package/modules/connectors/src/client/ConnectorCallsView.tsrx +358 -0
  366. package/modules/connectors/src/client/ConnectorForm.tsrx +424 -0
  367. package/modules/connectors/src/client/ConnectorsView.tsrx +711 -0
  368. package/modules/connectors/src/client/api.ts +281 -0
  369. package/modules/connectors/src/client/consent.ts +48 -0
  370. package/modules/connectors/src/client/contribution.tsrx +37 -0
  371. package/modules/connectors/src/client/index.ts +22 -0
  372. package/modules/connectors/src/client/navigation.ts +42 -0
  373. package/modules/connectors/src/client/presentation.ts +38 -0
  374. package/modules/connectors/src/client/state.ts +175 -0
  375. package/modules/connectors/src/domain/calls.ts +70 -0
  376. package/modules/connectors/src/domain/definitions.ts +190 -0
  377. package/modules/connectors/src/domain/http-json.ts +68 -0
  378. package/modules/connectors/src/domain/types.ts +185 -0
  379. package/modules/connectors/src/index.ts +64 -0
  380. package/modules/connectors/src/platform.ts +56 -0
  381. package/modules/connectors/src/server/index.ts +66 -0
  382. package/modules/connectors/src/server/runtime.ts +163 -0
  383. package/modules/connectors/src/services/call-service.ts +903 -0
  384. package/modules/connectors/src/services/connectors-service.ts +653 -0
  385. package/modules/connectors/src/services/credential-rotation.ts +221 -0
  386. package/modules/connectors/src/services/credential-vault.ts +194 -0
  387. package/modules/connectors/src/services/data-classes.ts +158 -0
  388. package/modules/connectors/src/services/database-repository.ts +897 -0
  389. package/modules/connectors/src/services/egress.ts +261 -0
  390. package/modules/connectors/src/services/index.ts +19 -0
  391. package/modules/connectors/src/services/migration.ts +202 -0
  392. package/modules/connectors/src/services/repository.ts +175 -0
  393. package/modules/connectors/src/services/service-error.ts +46 -0
  394. package/modules/connectors/src/settings.ts +69 -0
  395. package/modules/connectors/translations/en.json +191 -0
  396. package/modules/connectors/translations/pl.json +191 -0
  397. package/modules/directory/migrations/0001_directory_core.down.sql +5 -0
  398. package/modules/directory/migrations/0001_directory_core.up.sql +111 -0
  399. package/modules/directory/migrations/README.md +3 -0
  400. package/modules/directory/module.json +27 -0
  401. package/modules/directory/package.json +46 -0
  402. package/modules/directory/spec/module.yaml +351 -0
  403. package/modules/directory/src/acl/permissions.ts +7 -0
  404. package/modules/directory/src/api/endpoints.ts +478 -0
  405. package/modules/directory/src/api/scim-endpoints.ts +516 -0
  406. package/modules/directory/src/client/GroupMappingsView.tsrx +430 -0
  407. package/modules/directory/src/client/ProvisioningLogView.tsrx +248 -0
  408. package/modules/directory/src/client/ScimTokensView.tsrx +587 -0
  409. package/modules/directory/src/client/api.ts +222 -0
  410. package/modules/directory/src/client/contribution.tsrx +46 -0
  411. package/modules/directory/src/client/index.ts +22 -0
  412. package/modules/directory/src/client/navigation.ts +57 -0
  413. package/modules/directory/src/client/presentation.ts +143 -0
  414. package/modules/directory/src/client/state.ts +173 -0
  415. package/modules/directory/src/domain/scim.ts +346 -0
  416. package/modules/directory/src/domain/types.ts +166 -0
  417. package/modules/directory/src/index.ts +44 -0
  418. package/modules/directory/src/platform.ts +43 -0
  419. package/modules/directory/src/server/index.ts +17 -0
  420. package/modules/directory/src/server/runtime.ts +124 -0
  421. package/modules/directory/src/services/auth-port.ts +127 -0
  422. package/modules/directory/src/services/data-classes.ts +41 -0
  423. package/modules/directory/src/services/database-repository.ts +1067 -0
  424. package/modules/directory/src/services/directory-service.ts +311 -0
  425. package/modules/directory/src/services/index.ts +43 -0
  426. package/modules/directory/src/services/migration.ts +146 -0
  427. package/modules/directory/src/services/provisioning-service.ts +1420 -0
  428. package/modules/directory/src/services/rate-limiter.ts +103 -0
  429. package/modules/directory/src/services/repository.ts +208 -0
  430. package/modules/directory/src/services/service-error.ts +42 -0
  431. package/modules/directory/src/services/token-service.ts +215 -0
  432. package/modules/directory/src/settings.ts +86 -0
  433. package/modules/directory/translations/en.json +185 -0
  434. package/modules/directory/translations/pl.json +185 -0
  435. package/modules/documents/migrations/0001_documents_core.down.sql +1 -0
  436. package/modules/documents/migrations/0001_documents_core.up.sql +35 -0
  437. package/modules/documents/migrations/0002_documents_files_page.down.sql +1 -0
  438. package/modules/documents/migrations/0002_documents_files_page.up.sql +6 -0
  439. package/modules/documents/migrations/0003_documents_rotation_inventory.down.sql +2 -0
  440. package/modules/documents/migrations/0003_documents_rotation_inventory.up.sql +18 -0
  441. package/modules/documents/migrations/README.md +3 -0
  442. package/modules/documents/module.json +32 -0
  443. package/modules/documents/package.json +50 -0
  444. package/modules/documents/spec/module.yaml +248 -0
  445. package/modules/documents/src/acl/permissions.ts +6 -0
  446. package/modules/documents/src/api/endpoints.ts +401 -0
  447. package/modules/documents/src/cli/commands.json +17 -0
  448. package/modules/documents/src/cli/index.ts +145 -0
  449. package/modules/documents/src/client/DocumentUploader.tsrx +148 -0
  450. package/modules/documents/src/client/DocumentsView.tsrx +540 -0
  451. package/modules/documents/src/client/api.ts +209 -0
  452. package/modules/documents/src/client/contribution.tsrx +29 -0
  453. package/modules/documents/src/client/download.ts +70 -0
  454. package/modules/documents/src/client/index.ts +22 -0
  455. package/modules/documents/src/client/navigation.ts +25 -0
  456. package/modules/documents/src/client/presentation.ts +110 -0
  457. package/modules/documents/src/client/state.ts +117 -0
  458. package/modules/documents/src/domain/attachments.ts +84 -0
  459. package/modules/documents/src/domain/types.ts +80 -0
  460. package/modules/documents/src/index.ts +46 -0
  461. package/modules/documents/src/platform.ts +61 -0
  462. package/modules/documents/src/server/index.ts +30 -0
  463. package/modules/documents/src/server/runtime.ts +107 -0
  464. package/modules/documents/src/services/attachments.ts +39 -0
  465. package/modules/documents/src/services/data-classes.ts +25 -0
  466. package/modules/documents/src/services/database-repository.ts +270 -0
  467. package/modules/documents/src/services/documents-service.ts +590 -0
  468. package/modules/documents/src/services/index.ts +18 -0
  469. package/modules/documents/src/services/migration.ts +117 -0
  470. package/modules/documents/src/services/repository.ts +58 -0
  471. package/modules/documents/src/services/storage-rotation.ts +157 -0
  472. package/modules/documents/src/settings.ts +107 -0
  473. package/modules/documents/translations/en.json +111 -0
  474. package/modules/documents/translations/pl.json +111 -0
  475. package/modules/exports/migrations/0001_exports_core.down.sql +2 -0
  476. package/modules/exports/migrations/0001_exports_core.up.sql +28 -0
  477. package/modules/exports/migrations/0002_exports_job_routing_role.down.sql +4 -0
  478. package/modules/exports/migrations/0002_exports_job_routing_role.up.sql +21 -0
  479. package/modules/exports/migrations/0003_exports_rotation_inventory.down.sql +1 -0
  480. package/modules/exports/migrations/0003_exports_rotation_inventory.up.sql +9 -0
  481. package/modules/exports/migrations/README.md +3 -0
  482. package/modules/exports/module.json +32 -0
  483. package/modules/exports/package.json +45 -0
  484. package/modules/exports/spec/module.yaml +230 -0
  485. package/modules/exports/src/acl/permissions.ts +6 -0
  486. package/modules/exports/src/api/endpoints.ts +211 -0
  487. package/modules/exports/src/cli/commands.json +17 -0
  488. package/modules/exports/src/cli/index.ts +145 -0
  489. package/modules/exports/src/client/ExportsView.tsrx +327 -0
  490. package/modules/exports/src/client/api.ts +133 -0
  491. package/modules/exports/src/client/contribution.tsrx +31 -0
  492. package/modules/exports/src/client/index.ts +20 -0
  493. package/modules/exports/src/client/navigation.ts +25 -0
  494. package/modules/exports/src/client/presentation.ts +53 -0
  495. package/modules/exports/src/client/state.ts +56 -0
  496. package/modules/exports/src/domain/lists.ts +29 -0
  497. package/modules/exports/src/domain/types.ts +120 -0
  498. package/modules/exports/src/index.ts +41 -0
  499. package/modules/exports/src/platform.ts +80 -0
  500. package/modules/exports/src/server/index.ts +50 -0
  501. package/modules/exports/src/server/runtime.ts +160 -0
  502. package/modules/exports/src/services/data-classes.ts +59 -0
  503. package/modules/exports/src/services/database-repository.ts +403 -0
  504. package/modules/exports/src/services/export-runner.ts +123 -0
  505. package/modules/exports/src/services/export-service.ts +356 -0
  506. package/modules/exports/src/services/index.ts +6 -0
  507. package/modules/exports/src/services/list-registry.ts +105 -0
  508. package/modules/exports/src/services/migration.ts +116 -0
  509. package/modules/exports/src/services/repository.ts +96 -0
  510. package/modules/exports/src/services/storage-rotation.ts +138 -0
  511. package/modules/exports/src/settings.ts +70 -0
  512. package/modules/exports/translations/en.json +69 -0
  513. package/modules/exports/translations/pl.json +69 -0
  514. package/modules/import/migrations/0001_import_core.down.sql +6 -0
  515. package/modules/import/migrations/0001_import_core.up.sql +61 -0
  516. package/modules/import/migrations/0002_import_job_routing_role.down.sql +4 -0
  517. package/modules/import/migrations/0002_import_job_routing_role.up.sql +20 -0
  518. package/modules/import/migrations/0003_import_jobs_export_index.down.sql +1 -0
  519. package/modules/import/migrations/0003_import_jobs_export_index.up.sql +7 -0
  520. package/modules/import/migrations/0004_import_jobs_traceparent.down.sql +1 -0
  521. package/modules/import/migrations/0004_import_jobs_traceparent.up.sql +7 -0
  522. package/modules/import/migrations/README.md +3 -0
  523. package/modules/import/module.json +37 -0
  524. package/modules/import/package.json +50 -0
  525. package/modules/import/spec/module.yaml +335 -0
  526. package/modules/import/src/acl/permissions.ts +6 -0
  527. package/modules/import/src/api/endpoints.ts +400 -0
  528. package/modules/import/src/client/ImportJobDrawer.tsrx +229 -0
  529. package/modules/import/src/client/ImportsView.tsrx +455 -0
  530. package/modules/import/src/client/NewImportForm.tsrx +274 -0
  531. package/modules/import/src/client/api.ts +243 -0
  532. package/modules/import/src/client/contribution.tsrx +29 -0
  533. package/modules/import/src/client/index.ts +23 -0
  534. package/modules/import/src/client/navigation.ts +25 -0
  535. package/modules/import/src/client/presentation.ts +59 -0
  536. package/modules/import/src/client/state.ts +43 -0
  537. package/modules/import/src/domain/csv.ts +258 -0
  538. package/modules/import/src/domain/ports.ts +157 -0
  539. package/modules/import/src/domain/types.ts +179 -0
  540. package/modules/import/src/index.ts +58 -0
  541. package/modules/import/src/platform.ts +60 -0
  542. package/modules/import/src/server/index.ts +49 -0
  543. package/modules/import/src/server/runtime.ts +158 -0
  544. package/modules/import/src/services/csv-source.ts +121 -0
  545. package/modules/import/src/services/data-classes.ts +63 -0
  546. package/modules/import/src/services/database-repository.ts +780 -0
  547. package/modules/import/src/services/import-runner.ts +127 -0
  548. package/modules/import/src/services/import-service.ts +872 -0
  549. package/modules/import/src/services/index.ts +6 -0
  550. package/modules/import/src/services/migration.ts +156 -0
  551. package/modules/import/src/services/port-registry.ts +158 -0
  552. package/modules/import/src/services/repository.ts +147 -0
  553. package/modules/import/src/settings.ts +70 -0
  554. package/modules/import/translations/en.json +175 -0
  555. package/modules/import/translations/pl.json +175 -0
  556. package/modules/metering/migrations/0001_metering_core.down.sql +6 -0
  557. package/modules/metering/migrations/0001_metering_core.up.sql +119 -0
  558. package/modules/metering/migrations/0002_metering_bucket_export.down.sql +3 -0
  559. package/modules/metering/migrations/0002_metering_bucket_export.up.sql +5 -0
  560. package/modules/metering/migrations/README.md +3 -0
  561. package/modules/metering/module.json +46 -0
  562. package/modules/metering/package.json +50 -0
  563. package/modules/metering/spec/module.yaml +269 -0
  564. package/modules/metering/src/acl/permissions.ts +5 -0
  565. package/modules/metering/src/api/endpoints.ts +104 -0
  566. package/modules/metering/src/cli/commands.json +28 -0
  567. package/modules/metering/src/cli/index.ts +246 -0
  568. package/modules/metering/src/client/LimitsView.tsrx +170 -0
  569. package/modules/metering/src/client/UsageSummaryWidget.tsrx +63 -0
  570. package/modules/metering/src/client/UsageView.tsrx +205 -0
  571. package/modules/metering/src/client/api.ts +91 -0
  572. package/modules/metering/src/client/contribution.tsrx +42 -0
  573. package/modules/metering/src/client/index.ts +26 -0
  574. package/modules/metering/src/client/navigation.ts +54 -0
  575. package/modules/metering/src/client/presentation.ts +63 -0
  576. package/modules/metering/src/client/state.ts +31 -0
  577. package/modules/metering/src/domain/meters.ts +100 -0
  578. package/modules/metering/src/domain/types.ts +71 -0
  579. package/modules/metering/src/index.ts +51 -0
  580. package/modules/metering/src/platform.ts +84 -0
  581. package/modules/metering/src/server/index.ts +40 -0
  582. package/modules/metering/src/server/runtime.ts +165 -0
  583. package/modules/metering/src/services/data-classes.ts +34 -0
  584. package/modules/metering/src/services/database-repository.ts +574 -0
  585. package/modules/metering/src/services/index.ts +9 -0
  586. package/modules/metering/src/services/meter-registry.ts +145 -0
  587. package/modules/metering/src/services/metering-service.ts +441 -0
  588. package/modules/metering/src/services/migration.ts +183 -0
  589. package/modules/metering/src/services/notifications.ts +75 -0
  590. package/modules/metering/src/services/reports.ts +105 -0
  591. package/modules/metering/src/services/repository.ts +132 -0
  592. package/modules/metering/src/services/service-error.ts +46 -0
  593. package/modules/metering/src/settings.ts +47 -0
  594. package/modules/metering/translations/en.json +68 -0
  595. package/modules/metering/translations/pl.json +68 -0
  596. package/modules/notifications/migrations/0001_notifications_core.down.sql +1 -0
  597. package/modules/notifications/migrations/0001_notifications_core.up.sql +26 -0
  598. package/modules/notifications/migrations/0002_notifications_preferences.down.sql +1 -0
  599. package/modules/notifications/migrations/0002_notifications_preferences.up.sql +17 -0
  600. package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.down.sql +1 -0
  601. package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.up.sql +30 -0
  602. package/modules/notifications/migrations/0004_notifications_deliveries.down.sql +1 -0
  603. package/modules/notifications/migrations/0004_notifications_deliveries.up.sql +35 -0
  604. package/modules/notifications/migrations/0005_notifications_delivery_routing_role.down.sql +2 -0
  605. package/modules/notifications/migrations/0005_notifications_delivery_routing_role.up.sql +17 -0
  606. package/modules/notifications/migrations/0006_notifications_delivery_title.down.sql +1 -0
  607. package/modules/notifications/migrations/0006_notifications_delivery_title.up.sql +11 -0
  608. package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.down.sql +2 -0
  609. package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.up.sql +19 -0
  610. package/modules/notifications/migrations/0008_notifications_delivery_claim.down.sql +8 -0
  611. package/modules/notifications/migrations/0008_notifications_delivery_claim.up.sql +19 -0
  612. package/modules/notifications/migrations/0009_notifications_kind_approvals.down.sql +16 -0
  613. package/modules/notifications/migrations/0009_notifications_kind_approvals.up.sql +18 -0
  614. package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.down.sql +16 -0
  615. package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.up.sql +18 -0
  616. package/modules/notifications/migrations/0011_notifications_retention_keyset.down.sql +5 -0
  617. package/modules/notifications/migrations/0011_notifications_retention_keyset.up.sql +15 -0
  618. package/modules/notifications/migrations/0012_notifications_email_channel.down.sql +12 -0
  619. package/modules/notifications/migrations/0012_notifications_email_channel.up.sql +38 -0
  620. package/modules/notifications/migrations/0013_notifications_list_pages.down.sql +5 -0
  621. package/modules/notifications/migrations/0013_notifications_list_pages.up.sql +13 -0
  622. package/modules/notifications/migrations/README.md +3 -0
  623. package/modules/notifications/module.json +32 -0
  624. package/modules/notifications/package.json +49 -0
  625. package/modules/notifications/spec/module.yaml +718 -0
  626. package/modules/notifications/src/acl/permissions.ts +11 -0
  627. package/modules/notifications/src/api/endpoints.ts +814 -0
  628. package/modules/notifications/src/cli/commands.json +17 -0
  629. package/modules/notifications/src/cli/index.ts +126 -0
  630. package/modules/notifications/src/client/DeliveriesView.tsrx +483 -0
  631. package/modules/notifications/src/client/FactList.tsrx +24 -0
  632. package/modules/notifications/src/client/NotificationPreferencesView.tsrx +240 -0
  633. package/modules/notifications/src/client/NotificationsInboxView.tsrx +592 -0
  634. package/modules/notifications/src/client/UnreadCountWidget.tsrx +62 -0
  635. package/modules/notifications/src/client/WebhookForm.tsrx +297 -0
  636. package/modules/notifications/src/client/WebhooksView.tsrx +637 -0
  637. package/modules/notifications/src/client/api.ts +382 -0
  638. package/modules/notifications/src/client/contribution.tsrx +83 -0
  639. package/modules/notifications/src/client/inbox.ts +41 -0
  640. package/modules/notifications/src/client/index.ts +29 -0
  641. package/modules/notifications/src/client/navigation.ts +92 -0
  642. package/modules/notifications/src/client/notifications.css +70 -0
  643. package/modules/notifications/src/client/presentation.ts +81 -0
  644. package/modules/notifications/src/client/state.ts +246 -0
  645. package/modules/notifications/src/client/unread-count.ts +26 -0
  646. package/modules/notifications/src/domain/locale.ts +47 -0
  647. package/modules/notifications/src/domain/publish.ts +46 -0
  648. package/modules/notifications/src/domain/types.ts +186 -0
  649. package/modules/notifications/src/index.ts +62 -0
  650. package/modules/notifications/src/platform.ts +85 -0
  651. package/modules/notifications/src/server/index.ts +82 -0
  652. package/modules/notifications/src/server/runtime.ts +251 -0
  653. package/modules/notifications/src/services/data-classes.ts +139 -0
  654. package/modules/notifications/src/services/database-repository.ts +1334 -0
  655. package/modules/notifications/src/services/delivery-payload.ts +93 -0
  656. package/modules/notifications/src/services/delivery-runner.ts +170 -0
  657. package/modules/notifications/src/services/delivery-service.ts +811 -0
  658. package/modules/notifications/src/services/egress.ts +239 -0
  659. package/modules/notifications/src/services/email-channel.ts +93 -0
  660. package/modules/notifications/src/services/index.ts +23 -0
  661. package/modules/notifications/src/services/migration.ts +572 -0
  662. package/modules/notifications/src/services/notifications-service.ts +297 -0
  663. package/modules/notifications/src/services/paging.ts +60 -0
  664. package/modules/notifications/src/services/publish-service.ts +133 -0
  665. package/modules/notifications/src/services/repository.ts +342 -0
  666. package/modules/notifications/src/services/secret-rotation.ts +177 -0
  667. package/modules/notifications/src/services/secret-vault.ts +168 -0
  668. package/modules/notifications/src/services/service-error.ts +67 -0
  669. package/modules/notifications/src/services/signature.ts +81 -0
  670. package/modules/notifications/src/services/webhook-service.ts +307 -0
  671. package/modules/notifications/src/settings.ts +130 -0
  672. package/modules/notifications/translations/en.json +255 -0
  673. package/modules/notifications/translations/pl.json +255 -0
  674. package/modules/profile/module.json +3 -3
  675. package/modules/profile/package.json +1 -1
  676. package/modules/profile/spec/module.yaml +3 -3
  677. package/modules/profile/src/services/database-repository.ts +3 -13
  678. package/modules/reports/module.json +28 -0
  679. package/modules/reports/package.json +42 -0
  680. package/modules/reports/spec/module.yaml +231 -0
  681. package/modules/reports/src/acl/permissions.ts +5 -0
  682. package/modules/reports/src/api/endpoints.ts +72 -0
  683. package/modules/reports/src/client/ReportsSummaryWidget.tsrx +52 -0
  684. package/modules/reports/src/client/ReportsView.tsrx +246 -0
  685. package/modules/reports/src/client/api.ts +65 -0
  686. package/modules/reports/src/client/contribution.tsrx +36 -0
  687. package/modules/reports/src/client/index.ts +18 -0
  688. package/modules/reports/src/client/navigation.ts +39 -0
  689. package/modules/reports/src/client/presentation.ts +123 -0
  690. package/modules/reports/src/client/state.ts +28 -0
  691. package/modules/reports/src/domain/providers.ts +115 -0
  692. package/modules/reports/src/domain/types.ts +44 -0
  693. package/modules/reports/src/index.ts +47 -0
  694. package/modules/reports/src/platform.ts +37 -0
  695. package/modules/reports/src/server/index.ts +21 -0
  696. package/modules/reports/src/server/runtime.ts +40 -0
  697. package/modules/reports/src/services/index.ts +17 -0
  698. package/modules/reports/src/services/provider-registry.ts +144 -0
  699. package/modules/reports/src/services/range.ts +65 -0
  700. package/modules/reports/src/services/reports-service.ts +316 -0
  701. package/modules/reports/src/services/service-error.ts +26 -0
  702. package/modules/reports/src/settings.ts +49 -0
  703. package/modules/reports/translations/en.json +35 -0
  704. package/modules/reports/translations/pl.json +35 -0
  705. package/modules/sandbox/module.json +4 -4
  706. package/modules/sandbox/package.json +1 -1
  707. package/modules/sandbox/spec/module.yaml +3 -3
  708. package/modules/sandbox/src/services/database-repository.ts +14 -23
  709. package/modules/sandbox/src/services/directory.ts +16 -0
  710. package/modules/sandbox/src/services/sandbox-service.ts +13 -11
  711. package/modules/search/migrations/0001_search_core.down.sql +1 -0
  712. package/modules/search/migrations/0001_search_core.up.sql +22 -0
  713. package/modules/search/migrations/0002_search_recent_export.down.sql +3 -0
  714. package/modules/search/migrations/0002_search_recent_export.up.sql +5 -0
  715. package/modules/search/migrations/README.md +3 -0
  716. package/modules/search/module.json +28 -0
  717. package/modules/search/package.json +43 -0
  718. package/modules/search/spec/module.yaml +177 -0
  719. package/modules/search/src/acl/permissions.ts +5 -0
  720. package/modules/search/src/api/endpoints.ts +221 -0
  721. package/modules/search/src/client/SearchView.tsrx +346 -0
  722. package/modules/search/src/client/api.ts +157 -0
  723. package/modules/search/src/client/contribution.tsrx +31 -0
  724. package/modules/search/src/client/index.ts +17 -0
  725. package/modules/search/src/client/navigation.ts +97 -0
  726. package/modules/search/src/client/search.css +56 -0
  727. package/modules/search/src/client/state.ts +113 -0
  728. package/modules/search/src/domain/data-classes.ts +73 -0
  729. package/modules/search/src/domain/providers.ts +88 -0
  730. package/modules/search/src/domain/types.ts +56 -0
  731. package/modules/search/src/index.ts +46 -0
  732. package/modules/search/src/platform.ts +42 -0
  733. package/modules/search/src/server/index.ts +25 -0
  734. package/modules/search/src/server/runtime.ts +123 -0
  735. package/modules/search/src/services/database-repository.ts +163 -0
  736. package/modules/search/src/services/index.ts +21 -0
  737. package/modules/search/src/services/migration.ts +65 -0
  738. package/modules/search/src/services/provider-registry.ts +137 -0
  739. package/modules/search/src/services/repository.ts +43 -0
  740. package/modules/search/src/services/search-service.ts +442 -0
  741. package/modules/search/src/services/service-error.ts +35 -0
  742. package/modules/search/src/settings.ts +68 -0
  743. package/modules/search/translations/en.json +45 -0
  744. package/modules/search/translations/pl.json +45 -0
  745. package/modules/system/module.json +2 -2
  746. package/modules/system/package.json +1 -1
  747. package/modules/system/spec/module.yaml +30 -3
  748. package/modules/system/src/client/FlagsPanel.tsrx +127 -0
  749. package/modules/system/src/client/ModuleSettingRow.tsrx +17 -7
  750. package/modules/system/src/client/ModuleSettingsSection.tsrx +10 -71
  751. package/modules/system/src/client/ModulesView.tsrx +91 -65
  752. package/modules/system/src/client/flags.ts +35 -0
  753. package/modules/system/src/client/index.ts +8 -0
  754. package/modules/system/src/client/navigation.ts +8 -0
  755. package/modules/system/src/client/settings-state.ts +159 -0
  756. package/modules/system/src/client/state.ts +2 -0
  757. package/modules/system/src/domain/time-zone.ts +105 -0
  758. package/modules/system/src/index.ts +12 -0
  759. package/modules/system/src/platform.ts +5 -1
  760. package/modules/system/src/server/endpoints.ts +72 -21
  761. package/modules/system/src/settings.ts +28 -0
  762. package/modules/system/translations/en.json +17 -0
  763. package/modules/system/translations/pl.json +17 -0
  764. package/modules/users/module.json +30 -4
  765. package/modules/users/package.json +4 -1
  766. package/modules/users/spec/module.yaml +290 -7
  767. package/modules/users/src/api/endpoints.ts +228 -15
  768. package/modules/users/src/client/InviteMemberForm.tsrx +2 -2
  769. package/modules/users/src/client/MemberDrawer.tsrx +142 -26
  770. package/modules/users/src/client/UserAccountForm.tsrx +2 -2
  771. package/modules/users/src/client/UsersView.tsrx +432 -71
  772. package/modules/users/src/client/api.ts +106 -12
  773. package/modules/users/src/client/contribution.tsrx +5 -0
  774. package/modules/users/src/client/member-columns.tsrx +22 -9
  775. package/modules/users/src/client/state.ts +172 -7
  776. package/modules/users/src/domain/lists.ts +9 -0
  777. package/modules/users/src/index.ts +11 -0
  778. package/modules/users/src/platform.ts +50 -1
  779. package/modules/users/src/services/member-export.ts +51 -0
  780. package/modules/users/src/services/member-import.ts +249 -0
  781. package/modules/users/src/services/member-search.ts +145 -0
  782. package/modules/users/src/services/users-service.ts +117 -4
  783. package/modules/users/translations/en.json +57 -8
  784. package/modules/users/translations/pl.json +57 -8
  785. package/modules/workflows/migrations/0004_payload_rotation_inventory.up.sql +17 -0
  786. package/modules/workflows/migrations/0005_workflows_human_approval.up.sql +28 -0
  787. package/modules/workflows/migrations/0006_workflows_retention_indexes.up.sql +11 -0
  788. package/modules/workflows/migrations/0007_workflows_erasure_subject_indexes.up.sql +17 -0
  789. package/modules/workflows/migrations/0008_workflows_subject_account.up.sql +24 -0
  790. package/modules/workflows/migrations/0009_workflows_definition_list_indexes.up.sql +9 -0
  791. package/modules/workflows/module.json +34 -6
  792. package/modules/workflows/package.json +2 -1
  793. package/modules/workflows/spec/module.yaml +26 -9
  794. package/modules/workflows/src/api/endpoints.ts +131 -25
  795. package/modules/workflows/src/cli/commands.json +17 -0
  796. package/modules/workflows/src/cli/index.ts +114 -0
  797. package/modules/workflows/src/client/WorkflowExecutionHistory.tsrx +36 -27
  798. package/modules/workflows/src/client/WorkflowInspector.tsrx +116 -0
  799. package/modules/workflows/src/client/WorkflowNodePalette.tsrx +1 -0
  800. package/modules/workflows/src/client/WorkflowsView.tsrx +236 -99
  801. package/modules/workflows/src/client/api.ts +54 -34
  802. package/modules/workflows/src/client/canvas-model.ts +12 -0
  803. package/modules/workflows/src/client/state.ts +95 -0
  804. package/modules/workflows/src/domain/events.ts +25 -11
  805. package/modules/workflows/src/domain/graph.ts +135 -1
  806. package/modules/workflows/src/domain/types.ts +91 -4
  807. package/modules/workflows/src/platform.ts +12 -0
  808. package/modules/workflows/src/server/runtime.ts +78 -5
  809. package/modules/workflows/src/services/approvals.ts +62 -0
  810. package/modules/workflows/src/services/cursors.ts +37 -0
  811. package/modules/workflows/src/services/data-classes.ts +232 -0
  812. package/modules/workflows/src/services/database-repository.ts +660 -171
  813. package/modules/workflows/src/services/migration.ts +240 -0
  814. package/modules/workflows/src/services/notifications.ts +98 -0
  815. package/modules/workflows/src/services/payload-codec.ts +51 -28
  816. package/modules/workflows/src/services/payload-rotation.ts +171 -0
  817. package/modules/workflows/src/services/repository.ts +123 -8
  818. package/modules/workflows/src/services/simulation.ts +7 -0
  819. package/modules/workflows/src/services/worker.ts +446 -25
  820. package/modules/workflows/src/services/workflows-service.ts +264 -103
  821. package/modules/workflows/translations/en.json +12 -4
  822. package/modules/workflows/translations/pl.json +12 -4
  823. package/modules.json +48 -4
  824. package/package.json +64 -6
  825. package/packages/client/package.json +1 -0
  826. package/packages/client/src/ApplicationShell.tsrx +43 -0
  827. package/packages/client/src/contributions.ts +55 -0
  828. package/packages/client/src/i18n/locales/en.json +5 -0
  829. package/packages/client/src/i18n/locales/pl.json +5 -0
  830. package/packages/client/src/index.ts +13 -1
  831. package/packages/client/src/routing.ts +22 -0
  832. package/packages/client/src/shell/CommandPalette.tsrx +223 -11
  833. package/packages/client/src/shell/command-search.ts +126 -0
  834. package/packages/client/src/shell/command.ts +30 -0
  835. package/packages/client/src/shell/navigation.ts +12 -0
  836. package/packages/client/src/shell/palette-keys.ts +27 -0
  837. package/packages/client/src/shell/shell.css +7 -0
  838. package/packages/contracts/schemas/module-spec.schema.json +287 -1
  839. package/packages/contracts/schemas/module.schema.json +25 -0
  840. package/packages/contracts/src/index.ts +144 -1
  841. package/packages/database/src/backup.ts +180 -0
  842. package/packages/database/src/decoders.ts +25 -0
  843. package/packages/database/src/index.ts +17 -0
  844. package/packages/database/src/migrations.ts +7 -0
  845. package/packages/database/src/record-history.ts +5 -14
  846. package/packages/harness/src/index.ts +2 -0
  847. package/packages/harness/src/runtime.ts +378 -2
  848. package/packages/harness/src/tool-adapters.ts +20 -9
  849. package/packages/kernel/src/acl.ts +179 -0
  850. package/packages/kernel/src/approval-grant.ts +310 -0
  851. package/packages/kernel/src/capability-registry.ts +69 -2
  852. package/packages/kernel/src/data-class-registry.ts +354 -0
  853. package/packages/kernel/src/index.ts +85 -4
  854. package/packages/kernel/src/keyring.ts +208 -0
  855. package/packages/kernel/src/module-compatibility.ts +35 -4
  856. package/packages/kernel/src/module-registry.ts +49 -2
  857. package/packages/kernel/src/module-settings.ts +189 -27
  858. package/packages/server/package.json +2 -1
  859. package/packages/server/src/composition.ts +38 -0
  860. package/packages/server/src/endpoint.ts +104 -47
  861. package/packages/server/src/export/csv.ts +34 -0
  862. package/packages/server/src/export/definition.ts +346 -0
  863. package/packages/server/src/export/index.ts +61 -0
  864. package/packages/server/src/export/run.ts +116 -0
  865. package/packages/server/src/index.ts +155 -0
  866. package/packages/server/src/jobs/index.ts +93 -0
  867. package/packages/server/src/jobs/runner.ts +486 -0
  868. package/packages/server/src/log.ts +291 -0
  869. package/packages/server/src/mail/config.ts +184 -0
  870. package/packages/server/src/mail/contracts.ts +251 -0
  871. package/packages/server/src/mail/index.ts +31 -0
  872. package/packages/server/src/mail/port.ts +101 -0
  873. package/packages/server/src/mail/smtp.ts +191 -0
  874. package/packages/server/src/mail/template.ts +105 -0
  875. package/packages/server/src/metrics.ts +447 -0
  876. package/packages/server/src/pagination.ts +246 -0
  877. package/packages/server/src/trace/context.ts +103 -0
  878. package/packages/server/src/trace/egress.ts +66 -0
  879. package/packages/server/src/trace/error-sink.ts +296 -0
  880. package/packages/server/src/trace/exporter.ts +295 -0
  881. package/packages/server/src/trace/index.ts +60 -0
  882. package/packages/server/src/trace/job-sink.ts +172 -0
  883. package/packages/server/src/trace/tracer.ts +338 -0
  884. package/packages/server/src/web.ts +5 -1
  885. package/packages/storage/package.json +40 -0
  886. package/packages/storage/src/config.ts +189 -0
  887. package/packages/storage/src/content-type.ts +124 -0
  888. package/packages/storage/src/contracts.ts +112 -0
  889. package/packages/storage/src/envelope.ts +238 -0
  890. package/packages/storage/src/index.ts +49 -0
  891. package/packages/storage/src/local.ts +75 -0
  892. package/packages/storage/src/port.ts +262 -0
  893. package/packages/storage/src/read-token.ts +130 -0
  894. package/packages/storage/src/reseal.ts +128 -0
  895. package/packages/storage/src/s3.ts +144 -0
  896. package/packages/storage/src/scanner.ts +22 -0
  897. package/packages/storage/src/sigv4.ts +104 -0
  898. package/packages/storage/src/store.ts +13 -0
  899. package/packages/ui/package.json +1 -0
  900. package/packages/ui/src/components/Button.tsrx +3 -0
  901. package/packages/ui/src/components/ConfirmDialog.tsrx +13 -2
  902. package/packages/ui/src/components/DateField.tsrx +92 -0
  903. package/packages/ui/src/components/DatePicker.tsrx +525 -0
  904. package/packages/ui/src/components/DateRangeField.tsrx +93 -0
  905. package/packages/ui/src/components/Drawer.tsrx +10 -1
  906. package/packages/ui/src/components/FileUpload.tsrx +158 -0
  907. package/packages/ui/src/components/Pagination.tsrx +130 -0
  908. package/packages/ui/src/components/Select.tsrx +78 -0
  909. package/packages/ui/src/components/Table.tsrx +498 -81
  910. package/packages/ui/src/components/Tabs.tsrx +53 -0
  911. package/packages/ui/src/components/ToastHost.tsrx +48 -0
  912. package/packages/ui/src/components/calendar.ts +243 -0
  913. package/packages/ui/src/components/date-field.ts +62 -0
  914. package/packages/ui/src/components/file-upload.ts +49 -0
  915. package/packages/ui/src/components/focus-trap.ts +62 -0
  916. package/packages/ui/src/components/pagination.ts +68 -0
  917. package/packages/ui/src/components/table-sorting.ts +49 -0
  918. package/packages/ui/src/components/tabs.ts +61 -0
  919. package/packages/ui/src/components/toast-store.ts +116 -0
  920. package/packages/ui/src/icons/Icon.tsrx +5 -0
  921. package/packages/ui/src/index.ts +45 -0
  922. package/packages/ui/src/styles/components.css +598 -0
  923. package/modules/automations-workflows-integration/module.json +0 -27
  924. package/modules/automations-workflows-integration/spec/module.yaml +0 -93
  925. package/modules/automations-workflows-integration/src/index.ts +0 -14
  926. package/modules/automations-workflows-integration/src/platform.ts +0 -24
  927. package/modules/automations-workflows-integration/src/server/index.ts +0 -11
  928. package/modules/automations-workflows-integration/translations/en.json +0 -21
  929. package/modules/automations-workflows-integration/translations/pl.json +0 -21
  930. package/modules/workflows/src/services/cursor-codec.ts +0 -62
@@ -1,4 +1,5 @@
1
1
  import {
2
+ integer,
2
3
  runDatabaseMigrations,
3
4
  type DatabaseHandle,
4
5
  type DatabaseParameter,
@@ -9,23 +10,30 @@ import type {
9
10
  ActorKind,
10
11
  ModuleSettingRecord,
11
12
  ModuleSettingValue,
13
+ UserActor,
12
14
  } from '@flowdular/sdk/kernel';
15
+ import { keysetWhere } from '@flowdular/sdk/server';
13
16
  import { BUILTIN_ROLES } from '../acl/scopes.ts';
14
17
  import type {
15
18
  ApiTokenRecord,
19
+ AuditEvent,
16
20
  AuditQuery,
17
21
  AuthPrincipal,
18
22
  AuthSession,
19
23
  AuthTenantAccess,
24
+ IdentityProviderStatus,
25
+ MembershipStatus,
20
26
  SessionSummary,
21
27
  TenantRole,
22
28
  } from '../domain/types.ts';
23
29
  import { databaseMigrations } from './migration.ts';
24
30
  import {
25
31
  DuplicateAccountError,
32
+ DuplicateProviderKeyError,
26
33
  DuplicateRoleKeyError,
27
34
  DuplicateTenantSlugError,
28
35
  type AccountCredential,
36
+ type AccountIdentity,
29
37
  type AuditActorEvent,
30
38
  type AuditRecord,
31
39
  type AuthRepository,
@@ -35,13 +43,22 @@ import {
35
43
  type CreateRoleRecord,
36
44
  type CreateSessionRecord,
37
45
  type CreateTenantMembershipRecord,
46
+ type ExternalIdentityBinding,
47
+ type ExternalIdentityRecord,
48
+ type IdentityProviderPatch,
49
+ type IdentityProviderRecord,
38
50
  type MfaChallengeRecord,
39
51
  type PasswordResetTokenRecord,
52
+ type SessionExportRecord,
40
53
  type SignInFailureRecord,
41
54
  type TenantInvitationRecord,
42
55
  type TenantMember,
56
+ type TenantMemberSort,
57
+ type TenantMemberSortedPage,
58
+ type TenantMemberSortedRead,
43
59
  type TenantSummary,
44
60
  } from './repository.ts';
61
+ import type { SealedMfaSecret } from './totp.ts';
45
62
 
46
63
  /**
47
64
  * The tenant context an identity table is read and written under. Accounts,
@@ -52,6 +69,14 @@ import {
52
69
  */
53
70
  export const IDENTITY_TENANT_CONTEXT = 'auth.core:identity';
54
71
 
72
+ /**
73
+ * Rows one expired-session delete removes. The sweep walks a backlog of
74
+ * unknown size, and a single delete over it would hold the table for as long as
75
+ * it takes; a bounded batch keeps every statement short and leaves the rest to
76
+ * the next batch.
77
+ */
78
+ export const EXPIRED_SESSION_SWEEP_BATCH = 1_000;
79
+
55
80
  /**
56
81
  * Storage tenant of a platform-scoped setting. The kernel addresses it as the
57
82
  * empty string, which is not a tenant id a policy can be bound to, so the row
@@ -84,6 +109,7 @@ interface AccountRow {
84
109
  role: string;
85
110
  role_id: string | null;
86
111
  status: 'active' | 'disabled';
112
+ membership_status: MembershipStatus;
87
113
  password_change_required: number | bigint | string;
88
114
  }
89
115
 
@@ -105,11 +131,38 @@ interface TenantMemberRow {
105
131
  role: string;
106
132
  role_id: string | null;
107
133
  status: 'active' | 'disabled';
134
+ membership_status: MembershipStatus;
108
135
  password_change_required: number | bigint | string;
109
136
  scopes: string | null;
110
137
  created_at: number | bigint | string;
111
138
  }
112
139
 
140
+ interface SortedTenantMemberRow extends TenantMemberRow {
141
+ sort_value: string;
142
+ }
143
+
144
+ interface IdentityProviderRow {
145
+ id: string;
146
+ tenant_id: string;
147
+ key: string;
148
+ label: string;
149
+ issuer: string;
150
+ authorization_endpoint: string;
151
+ token_endpoint: string;
152
+ user_info_endpoint: string;
153
+ client_id: string;
154
+ client_secret_ciphertext: string;
155
+ client_secret_key_id: string;
156
+ client_secret_fingerprint: string;
157
+ scopes_json: string;
158
+ jit_enabled: number | bigint | string;
159
+ allowed_domains_json: string;
160
+ jit_role: string;
161
+ status: IdentityProviderStatus;
162
+ created_at: number | bigint | string;
163
+ updated_at: number | bigint | string;
164
+ }
165
+
113
166
  interface SessionRow extends AccountRow {
114
167
  session_id: string;
115
168
  csrf_token: string;
@@ -138,6 +191,15 @@ interface RoleRow {
138
191
  updated_at: number | bigint | string;
139
192
  }
140
193
 
194
+ interface SessionExportRow {
195
+ id: string;
196
+ tenant_id: string;
197
+ account_id: string;
198
+ created_at: number | bigint | string;
199
+ expires_at: number | bigint | string;
200
+ last_seen_at: number | bigint | string;
201
+ }
202
+
141
203
  interface AuditRow {
142
204
  id: number | bigint | string;
143
205
  tenant_id: string;
@@ -145,6 +207,7 @@ interface AuditRow {
145
207
  actor_label: string;
146
208
  actor_kind: ActorKind;
147
209
  actor_run_id: string | null;
210
+ configured_by_json: string | null;
148
211
  action: string;
149
212
  subject_type: string;
150
213
  subject_id: string;
@@ -162,26 +225,128 @@ interface CountRow {
162
225
  total: number | bigint | string;
163
226
  }
164
227
 
165
- /* PostgreSQL returns BIGINT as a string, and every timestamp, expiry and
166
- counter here is one. An unnormalized read would compare and serialize as
167
- text: a session would never look expired and a role would never look
168
- built in. */
169
- function integer(value: number | bigint | string): number {
170
- const normalized = Number(value);
171
- if (!Number.isSafeInteger(normalized)) {
172
- throw new Error('The auth database returned an invalid integer.');
173
- }
174
- return normalized;
175
- }
176
-
177
228
  function optionalInteger(
178
229
  value: number | bigint | string | null,
230
+ field: string,
179
231
  ): number | null {
180
- return value === null ? null : integer(value);
232
+ return value === null ? null : integer(value, field);
181
233
  }
182
234
 
183
235
  const ACCOUNT_COLUMNS = `a.id AS account_id, m.tenant_id, a.email, a.display_name,
184
- a.password_hash, m.role, m.role_id, a.status, a.password_change_required`;
236
+ a.password_hash, m.role, m.role_id, a.status,
237
+ m.status AS membership_status, a.password_change_required`;
238
+
239
+ /* One projection for the whole roll and for a single member, so the two reads
240
+ can never disagree about what a member is. */
241
+ const TENANT_MEMBER_COLUMNS = `a.id AS account_id, a.email, a.display_name,
242
+ m.role, m.role_id, a.status, m.status AS membership_status,
243
+ a.password_change_required, m.created_at,
244
+ (SELECT string_agg(s.scope, ' ' ORDER BY s.scope)
245
+ FROM auth_membership_scopes s
246
+ WHERE s.account_id = m.account_id AND s.tenant_id = m.tenant_id) AS scopes`;
247
+
248
+ /**
249
+ * The member search statement, exported so a plan assertion explains the
250
+ * statement the repository runs rather than a copy of it that can drift away
251
+ * from the indexes it was written for.
252
+ */
253
+ export const TENANT_MEMBER_SEARCH_SQL = `SELECT ${TENANT_MEMBER_COLUMNS}
254
+ FROM auth_memberships m
255
+ JOIN auth_accounts a ON a.id = m.account_id
256
+ WHERE m.tenant_id = $1
257
+ AND (lower(a.display_name) LIKE $2 ESCAPE '\\'
258
+ OR a.email_normalized LIKE $2 ESCAPE '\\')
259
+ ORDER BY lower(a.display_name), a.id
260
+ LIMIT $3`;
261
+
262
+ const TENANT_MEMBER_SORT_COLUMNS: Readonly<Record<TenantMemberSort, string>> = {
263
+ displayName: 'lower(a.display_name)',
264
+ email: 'a.email_normalized',
265
+ };
266
+
267
+ /**
268
+ * The sorted member page statement, exported for the same plan assertion as
269
+ * the search. The sort column is projected once, so the keyset predicate names
270
+ * a plain identifier and the ORDER BY the same expression the index carries.
271
+ */
272
+ export function tenantMemberSortedStatement(
273
+ tenantId: string,
274
+ read: TenantMemberSortedRead,
275
+ ): DatabaseStatement {
276
+ const parameters: DatabaseParameter[] = [tenantId];
277
+ const filters = ['m.tenant_id = $1'];
278
+ if (read.membershipStatus !== null) {
279
+ parameters.push(read.membershipStatus);
280
+ filters.push(`m.status = $${parameters.length}`);
281
+ }
282
+ if (read.term !== null) {
283
+ parameters.push(`${read.term}%`);
284
+ filters.push(
285
+ `(lower(a.display_name) LIKE $${parameters.length} ESCAPE '\\'
286
+ OR a.email_normalized LIKE $${parameters.length} ESCAPE '\\')`,
287
+ );
288
+ }
289
+ let keyset = '';
290
+ if (read.after !== null) {
291
+ const predicate = keysetWhere(
292
+ ['sort_value', 'account_id'],
293
+ [read.after.sortValue, read.after.accountId],
294
+ { direction: read.direction, parameterOffset: parameters.length },
295
+ );
296
+ parameters.push(...predicate.parameters);
297
+ keyset = `WHERE ${predicate.text}`;
298
+ }
299
+ parameters.push(read.limit);
300
+ const order = read.direction === 'asc' ? 'ASC' : 'DESC';
301
+ return {
302
+ text: `SELECT * FROM (
303
+ SELECT ${TENANT_MEMBER_COLUMNS},
304
+ ${TENANT_MEMBER_SORT_COLUMNS[read.sort]} AS sort_value
305
+ FROM auth_memberships m
306
+ JOIN auth_accounts a ON a.id = m.account_id
307
+ WHERE ${filters.join(' AND ')}
308
+ ) members
309
+ ${keyset}
310
+ ORDER BY sort_value ${order}, account_id ${order}
311
+ LIMIT $${parameters.length}`,
312
+ parameters,
313
+ };
314
+ }
315
+
316
+ /**
317
+ * One PostgreSQL `text[]` literal. A bound parameter carries no array type, so
318
+ * a list travels as this literal and the statement casts it back. Every element
319
+ * is quoted with its backslashes and quotes escaped, so no element can close
320
+ * the literal early or add one of its own.
321
+ */
322
+ export function textArrayLiteral(values: readonly string[]): string {
323
+ return `{${values
324
+ .map((value) => `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`)
325
+ .join(',')}}`;
326
+ }
327
+
328
+ interface ExternalIdentityBindingRow {
329
+ account_id: string;
330
+ provider: string;
331
+ subject: string;
332
+ created_at: number | bigint | string;
333
+ }
334
+
335
+ function tenantMemberFrom(row: TenantMemberRow): TenantMember {
336
+ return {
337
+ accountId: row.account_id,
338
+ email: row.email,
339
+ displayName: row.display_name,
340
+ role: row.role,
341
+ roleId: row.role_id,
342
+ status: row.status,
343
+ membershipStatus: row.membership_status,
344
+ scopes: row.scopes ? row.scopes.split(' ') : [],
345
+ passwordChangeRequired:
346
+ integer(row.password_change_required, 'password_change_required') === 1,
347
+ createdAt: integer(row.created_at, 'created_at'),
348
+ };
349
+ }
185
350
 
186
351
  export function builtinRoleId(tenantId: string, key: string): string {
187
352
  return `${tenantId}:${key}`;
@@ -196,10 +361,10 @@ function fromApiTokenRow(row: ApiTokenRow): ApiTokenRecord {
196
361
  prefix: row.prefix,
197
362
  scopes: JSON.parse(row.scopes_json) as readonly string[],
198
363
  createdBy: row.created_by,
199
- createdAt: integer(row.created_at),
200
- expiresAt: optionalInteger(row.expires_at),
201
- lastUsedAt: optionalInteger(row.last_used_at),
202
- revokedAt: optionalInteger(row.revoked_at),
364
+ createdAt: integer(row.created_at, 'created_at'),
365
+ expiresAt: optionalInteger(row.expires_at, 'expires_at'),
366
+ lastUsedAt: optionalInteger(row.last_used_at, 'last_used_at'),
367
+ revokedAt: optionalInteger(row.revoked_at, 'revoked_at'),
203
368
  revokedBy: row.revoked_by,
204
369
  };
205
370
  }
@@ -212,9 +377,35 @@ function fromRoleRow(row: RoleRow): TenantRole {
212
377
  name: row.name,
213
378
  description: row.description,
214
379
  scopes: JSON.parse(row.scopes_json) as readonly string[],
215
- builtin: integer(row.builtin) === 1,
216
- createdAt: integer(row.created_at),
217
- updatedAt: integer(row.updated_at),
380
+ builtin: integer(row.builtin, 'builtin') === 1,
381
+ createdAt: integer(row.created_at, 'created_at'),
382
+ updatedAt: integer(row.updated_at, 'updated_at'),
383
+ };
384
+ }
385
+
386
+ function fromIdentityProviderRow(
387
+ row: IdentityProviderRow,
388
+ ): IdentityProviderRecord {
389
+ return {
390
+ id: row.id,
391
+ tenantId: row.tenant_id,
392
+ key: row.key,
393
+ label: row.label,
394
+ issuer: row.issuer,
395
+ authorizationEndpoint: row.authorization_endpoint,
396
+ tokenEndpoint: row.token_endpoint,
397
+ userInfoEndpoint: row.user_info_endpoint,
398
+ clientId: row.client_id,
399
+ secretCiphertext: row.client_secret_ciphertext,
400
+ secretKeyId: row.client_secret_key_id,
401
+ secretFingerprint: row.client_secret_fingerprint,
402
+ scopes: JSON.parse(row.scopes_json) as readonly string[],
403
+ jitEnabled: integer(row.jit_enabled, 'jit_enabled') === 1,
404
+ allowedDomains: JSON.parse(row.allowed_domains_json) as readonly string[],
405
+ jitRole: row.jit_role,
406
+ status: row.status,
407
+ createdAt: integer(row.created_at, 'created_at'),
408
+ updatedAt: integer(row.updated_at, 'updated_at'),
218
409
  };
219
410
  }
220
411
 
@@ -225,11 +416,16 @@ function tenantSummary(row: TenantRow): TenantSummary {
225
416
  /* A unique violation arrives as the driver's own error. The constraint name is
226
417
  the only stable part of it, so the duplicate a caller can act on is
227
418
  recognized by name and everything else is rethrown untouched. */
228
- function duplicate(error: unknown): 'email' | 'role-key' | 'slug' | null {
419
+ function duplicate(
420
+ error: unknown,
421
+ ): 'email' | 'role-key' | 'slug' | 'provider-key' | null {
229
422
  const text = String(error);
230
423
  if (text.includes('auth_accounts_email_normalized_key')) return 'email';
231
424
  if (text.includes('auth_tenants_slug_idx')) return 'slug';
232
425
  if (text.includes('auth_roles_tenant_id_key_key')) return 'role-key';
426
+ if (text.includes('auth_identity_providers_tenant_id_key_key')) {
427
+ return 'provider-key';
428
+ }
233
429
  return null;
234
430
  }
235
431
 
@@ -318,9 +514,12 @@ export class DatabaseAuthRepository implements AuthRepository {
318
514
  return result.rows;
319
515
  }
320
516
 
517
+ /* A disabled membership answers for nothing: it must not be the workspace a
518
+ session cookie, a bearer token or a sign-in resolves to. */
321
519
  async #tenantOfAccount(accountId: string): Promise<string | null> {
322
520
  const rows = await this.#route<RoutedTenantRow>({
323
- text: `SELECT tenant_id FROM auth_memberships WHERE account_id = $1
521
+ text: `SELECT tenant_id FROM auth_memberships
522
+ WHERE account_id = $1 AND status = 'active'
324
523
  ORDER BY created_at, tenant_id LIMIT 1`,
325
524
  parameters: [accountId],
326
525
  });
@@ -353,7 +552,9 @@ export class DatabaseAuthRepository implements AuthRepository {
353
552
  role: row.role,
354
553
  roleId: row.role_id,
355
554
  status: row.status,
356
- passwordChangeRequired: integer(row.password_change_required) === 1,
555
+ membershipStatus: row.membership_status,
556
+ passwordChangeRequired:
557
+ integer(row.password_change_required, 'password_change_required') === 1,
357
558
  scopes: await this.#scopes(transaction, row.account_id, row.tenant_id),
358
559
  };
359
560
  }
@@ -501,6 +702,30 @@ export class DatabaseAuthRepository implements AuthRepository {
501
702
  return accountId ? this.findAccountCredentialById(accountId) : null;
502
703
  }
503
704
 
705
+ async findAccountIdentity(
706
+ normalizedEmail: string,
707
+ ): Promise<AccountIdentity | null> {
708
+ const rows = await this.#query<{
709
+ id: string;
710
+ email: string;
711
+ display_name: string;
712
+ status: 'active' | 'disabled';
713
+ }>(IDENTITY_TENANT_CONTEXT, {
714
+ text: `SELECT id, email, display_name, status FROM auth_accounts
715
+ WHERE email_normalized = $1`,
716
+ parameters: [normalizedEmail],
717
+ });
718
+ const row = rows[0];
719
+ return row
720
+ ? {
721
+ accountId: row.id,
722
+ email: row.email,
723
+ displayName: row.display_name,
724
+ status: row.status,
725
+ }
726
+ : null;
727
+ }
728
+
504
729
  async findAccountCredentialById(
505
730
  accountId: string,
506
731
  ): Promise<AccountCredential | null> {
@@ -611,7 +836,7 @@ export class DatabaseAuthRepository implements AuthRepository {
611
836
  text: 'SELECT count(*) AS total FROM auth_memberships WHERE account_id = $1',
612
837
  parameters: [accountId],
613
838
  });
614
- return rows[0] ? integer(rows[0].total) : 0;
839
+ return rows[0] ? integer(rows[0].total, 'total') : 0;
615
840
  }
616
841
 
617
842
  /* The account row is the identity. Its memberships, sessions and tokens go
@@ -628,10 +853,11 @@ export class DatabaseAuthRepository implements AuthRepository {
628
853
  const rows = await this.#query<CountRow>(tenantId, {
629
854
  text: `SELECT count(*) AS total FROM auth_memberships m
630
855
  JOIN auth_accounts a ON a.id = m.account_id
631
- WHERE m.tenant_id = $1 AND m.role = 'owner' AND a.status = 'active'`,
856
+ WHERE m.tenant_id = $1 AND m.role = 'owner'
857
+ AND a.status = 'active' AND m.status = 'active'`,
632
858
  parameters: [tenantId],
633
859
  });
634
- return rows[0] ? integer(rows[0].total) : 0;
860
+ return rows[0] ? integer(rows[0].total, 'total') : 0;
635
861
  }
636
862
 
637
863
  /* Which workspaces an account may enter is the one question that spans
@@ -643,7 +869,7 @@ export class DatabaseAuthRepository implements AuthRepository {
643
869
  text: `SELECT t.id AS tenant_id, t.name, t.slug, m.role
644
870
  FROM auth_memberships m
645
871
  JOIN auth_tenants t ON t.id = m.tenant_id
646
- WHERE m.account_id = $1
872
+ WHERE m.account_id = $1 AND m.status = 'active'
647
873
  ORDER BY m.created_at, t.id`,
648
874
  parameters: [accountId],
649
875
  });
@@ -657,28 +883,103 @@ export class DatabaseAuthRepository implements AuthRepository {
657
883
 
658
884
  async listTenantMembers(tenantId: string): Promise<readonly TenantMember[]> {
659
885
  const rows = await this.#query<TenantMemberRow>(tenantId, {
660
- text: `SELECT a.id AS account_id, a.email, a.display_name, m.role, m.role_id,
661
- a.status, a.password_change_required, m.created_at,
662
- (SELECT string_agg(s.scope, ' ' ORDER BY s.scope)
663
- FROM auth_membership_scopes s
664
- WHERE s.account_id = m.account_id AND s.tenant_id = m.tenant_id) AS scopes
886
+ text: `SELECT ${TENANT_MEMBER_COLUMNS}
665
887
  FROM auth_memberships m
666
888
  JOIN auth_accounts a ON a.id = m.account_id
667
889
  WHERE m.tenant_id = $1
668
890
  ORDER BY lower(a.display_name), a.id`,
669
891
  parameters: [tenantId],
670
892
  });
671
- return rows.map((row) => ({
672
- accountId: row.account_id,
673
- email: row.email,
674
- displayName: row.display_name,
675
- role: row.role,
676
- roleId: row.role_id,
677
- status: row.status,
678
- scopes: row.scopes ? row.scopes.split(' ') : [],
679
- passwordChangeRequired: integer(row.password_change_required) === 1,
680
- createdAt: integer(row.created_at),
681
- }));
893
+ return rows.map(tenantMemberFrom);
894
+ }
895
+
896
+ /* Keyset by account id, which migration 0030 indexes behind the workspace,
897
+ so a page is an index range scan of exactly the rows it returns rather
898
+ than a walk over every workspace's memberships after the cursor. */
899
+ async listTenantMembersPage(
900
+ tenantId: string,
901
+ afterAccountId: string,
902
+ limit: number,
903
+ ): Promise<readonly TenantMember[]> {
904
+ const rows = await this.#query<TenantMemberRow>(tenantId, {
905
+ text: `SELECT ${TENANT_MEMBER_COLUMNS}
906
+ FROM auth_memberships m
907
+ JOIN auth_accounts a ON a.id = m.account_id
908
+ WHERE m.tenant_id = $1 AND m.account_id > $2
909
+ ORDER BY m.account_id
910
+ LIMIT $3`,
911
+ parameters: [tenantId, afterAccountId, limit],
912
+ });
913
+ return rows.map(tenantMemberFrom);
914
+ }
915
+
916
+ async listTenantMembersSorted(
917
+ tenantId: string,
918
+ read: TenantMemberSortedRead,
919
+ ): Promise<TenantMemberSortedPage> {
920
+ const rows = await this.#query<SortedTenantMemberRow>(
921
+ tenantId,
922
+ tenantMemberSortedStatement(tenantId, read),
923
+ );
924
+ const last = rows[rows.length - 1];
925
+ return {
926
+ members: rows.map(tenantMemberFrom),
927
+ next:
928
+ last && rows.length === read.limit
929
+ ? { sortValue: last.sort_value, accountId: last.account_id }
930
+ : null,
931
+ };
932
+ }
933
+
934
+ async countTenantMembers(tenantId: string): Promise<number> {
935
+ const rows = await this.#query<CountRow>(tenantId, {
936
+ text: 'SELECT count(*) AS total FROM auth_memberships WHERE tenant_id = $1',
937
+ parameters: [tenantId],
938
+ });
939
+ return rows[0] ? integer(rows[0].total, 'total') : 0;
940
+ }
941
+
942
+ async findTenantMember(
943
+ tenantId: string,
944
+ accountId: string,
945
+ ): Promise<TenantMember | null> {
946
+ const rows = await this.#query<TenantMemberRow>(tenantId, {
947
+ text: `SELECT ${TENANT_MEMBER_COLUMNS}
948
+ FROM auth_memberships m
949
+ JOIN auth_accounts a ON a.id = m.account_id
950
+ WHERE m.tenant_id = $1 AND m.account_id = $2`,
951
+ parameters: [tenantId, accountId],
952
+ });
953
+ const row = rows[0];
954
+ return row ? tenantMemberFrom(row) : null;
955
+ }
956
+
957
+ async findTenantMembersByEmail(
958
+ tenantId: string,
959
+ normalizedEmails: readonly string[],
960
+ ): Promise<readonly TenantMember[]> {
961
+ if (normalizedEmails.length === 0) return [];
962
+ const rows = await this.#query<TenantMemberRow>(tenantId, {
963
+ text: `SELECT ${TENANT_MEMBER_COLUMNS}
964
+ FROM auth_memberships m
965
+ JOIN auth_accounts a ON a.id = m.account_id
966
+ WHERE m.tenant_id = $1 AND a.email_normalized = ANY($2::text[])
967
+ ORDER BY lower(a.display_name), a.id`,
968
+ parameters: [tenantId, textArrayLiteral(normalizedEmails)],
969
+ });
970
+ return rows.map(tenantMemberFrom);
971
+ }
972
+
973
+ async searchTenantMembers(
974
+ tenantId: string,
975
+ term: string,
976
+ limit: number,
977
+ ): Promise<readonly TenantMember[]> {
978
+ const rows = await this.#query<TenantMemberRow>(tenantId, {
979
+ text: TENANT_MEMBER_SEARCH_SQL,
980
+ parameters: [tenantId, `${term}%`, limit],
981
+ });
982
+ return rows.map(tenantMemberFrom);
682
983
  }
683
984
 
684
985
  async listTenantScopes(tenantId: string): Promise<readonly string[]> {
@@ -1139,12 +1440,13 @@ export class DatabaseAuthRepository implements AuthRepository {
1139
1440
  JOIN auth_accounts a ON a.id = s.account_id
1140
1441
  JOIN auth_memberships m
1141
1442
  ON m.account_id = s.account_id AND m.tenant_id = s.tenant_id
1142
- WHERE s.token_hash = $1 AND s.expires_at > $2 AND a.status = 'active'`,
1443
+ WHERE s.token_hash = $1 AND s.expires_at > $2 AND a.status = 'active'
1444
+ AND m.status = 'active'`,
1143
1445
  parameters: [tokenHash, now],
1144
1446
  });
1145
1447
  const row = result.rows[0];
1146
1448
  if (!row) return null;
1147
- const lastSeenAt = integer(row.last_seen_at);
1449
+ const lastSeenAt = integer(row.last_seen_at, 'last_seen_at');
1148
1450
  if (now - lastSeenAt > idleMs) {
1149
1451
  await transaction.execute({
1150
1452
  text: 'DELETE FROM auth_sessions WHERE token_hash = $1',
@@ -1176,10 +1478,13 @@ export class DatabaseAuthRepository implements AuthRepository {
1176
1478
  return {
1177
1479
  principal,
1178
1480
  csrfToken: session.row.csrf_token,
1179
- expiresAt: integer(session.row.expires_at),
1481
+ expiresAt: integer(session.row.expires_at, 'expires_at'),
1180
1482
  sessionId: session.row.session_id,
1181
1483
  passwordChangeRequired:
1182
- integer(session.row.password_change_required) === 1,
1484
+ integer(
1485
+ session.row.password_change_required,
1486
+ 'password_change_required',
1487
+ ) === 1,
1183
1488
  };
1184
1489
  }
1185
1490
 
@@ -1209,9 +1514,9 @@ export class DatabaseAuthRepository implements AuthRepository {
1209
1514
  id: row.id,
1210
1515
  tenantId: row.tenant_id,
1211
1516
  tenantName: row.tenant_name,
1212
- createdAt: integer(row.created_at),
1213
- lastSeenAt: integer(row.last_seen_at),
1214
- expiresAt: integer(row.expires_at),
1517
+ createdAt: integer(row.created_at, 'created_at'),
1518
+ lastSeenAt: integer(row.last_seen_at, 'last_seen_at'),
1519
+ expiresAt: integer(row.expires_at, 'expires_at'),
1215
1520
  });
1216
1521
  }
1217
1522
  }
@@ -1249,17 +1554,38 @@ export class DatabaseAuthRepository implements AuthRepository {
1249
1554
  return affected > 0;
1250
1555
  }
1251
1556
 
1252
- async deleteExpiredSessions(now: number): Promise<number> {
1557
+ /**
1558
+ * Removes sessions that expired before `now` in batches of
1559
+ * EXPIRED_SESSION_SWEEP_BATCH, at most `maxBatches` of them, and answers how
1560
+ * many rows went. A backlog of unknown size must not become one delete that
1561
+ * holds the table for the length of it, and what the bound leaves behind is
1562
+ * the next pass's work. Row security scopes a delete to one workspace, so
1563
+ * the rows are routed first and removed per tenant.
1564
+ */
1565
+ async deleteExpiredSessions(
1566
+ now: number,
1567
+ maxBatches: number = Number.POSITIVE_INFINITY,
1568
+ ): Promise<number> {
1253
1569
  const tenants = await this.#route<RoutedTenantRow>({
1254
1570
  text: 'SELECT DISTINCT tenant_id FROM auth_sessions WHERE expires_at <= $1',
1255
1571
  parameters: [now],
1256
1572
  });
1257
1573
  let removed = 0;
1574
+ let batches = 0;
1258
1575
  for (const tenant of tenants) {
1259
- removed += await this.#exec(tenant.tenant_id, {
1260
- text: 'DELETE FROM auth_sessions WHERE expires_at <= $1',
1261
- parameters: [now],
1262
- });
1576
+ while (batches < maxBatches) {
1577
+ batches += 1;
1578
+ const gone = await this.#exec(tenant.tenant_id, {
1579
+ text: `DELETE FROM auth_sessions
1580
+ WHERE id IN (SELECT id FROM auth_sessions
1581
+ WHERE expires_at <= $1
1582
+ LIMIT $2)`,
1583
+ parameters: [now, EXPIRED_SESSION_SWEEP_BATCH],
1584
+ });
1585
+ removed += gone;
1586
+ /* A short batch is the proof this workspace is drained. */
1587
+ if (gone < EXPIRED_SESSION_SWEEP_BATCH) break;
1588
+ }
1263
1589
  }
1264
1590
  return removed;
1265
1591
  }
@@ -1287,6 +1613,24 @@ export class DatabaseAuthRepository implements AuthRepository {
1287
1613
  });
1288
1614
  }
1289
1615
 
1616
+ /* Reads the account a live link names so the password policy can run on the
1617
+ full account before anything spends the token. Claiming it stays the job
1618
+ of consumePasswordResetToken below. */
1619
+ async findPasswordResetTokenAccount(
1620
+ tokenHash: string,
1621
+ now: number,
1622
+ ): Promise<string | null> {
1623
+ const rows = await this.#query<{ account_id: string }>(
1624
+ IDENTITY_TENANT_CONTEXT,
1625
+ {
1626
+ text: `SELECT account_id FROM auth_password_reset_tokens
1627
+ WHERE token_hash = $1 AND used_at IS NULL AND expires_at > $2`,
1628
+ parameters: [tokenHash, now],
1629
+ },
1630
+ );
1631
+ return rows[0]?.account_id ?? null;
1632
+ }
1633
+
1290
1634
  /* One statement claims the token: a second caller with the same link finds
1291
1635
  used_at already set and gets nothing. */
1292
1636
  async consumePasswordResetToken(
@@ -1378,30 +1722,33 @@ export class DatabaseAuthRepository implements AuthRepository {
1378
1722
 
1379
1723
  async upsertMfaTotp(
1380
1724
  accountId: string,
1381
- secretCiphertext: string,
1725
+ secret: SealedMfaSecret,
1382
1726
  createdAt: number,
1383
1727
  ): Promise<void> {
1384
1728
  await this.#exec(IDENTITY_TENANT_CONTEXT, {
1385
1729
  text: `INSERT INTO auth_mfa_totp
1386
- (account_id, secret_ciphertext, confirmed_at, created_at)
1387
- VALUES ($1, $2, NULL, $3)
1730
+ (account_id, secret_ciphertext, key_id, confirmed_at, created_at)
1731
+ VALUES ($1, $2, $3, NULL, $4)
1388
1732
  ON CONFLICT (account_id) DO UPDATE SET
1389
1733
  secret_ciphertext = excluded.secret_ciphertext,
1734
+ key_id = excluded.key_id,
1390
1735
  confirmed_at = NULL,
1391
1736
  created_at = excluded.created_at`,
1392
- parameters: [accountId, secretCiphertext, createdAt],
1737
+ parameters: [accountId, secret.ciphertext, secret.keyId, createdAt],
1393
1738
  });
1394
1739
  }
1395
1740
 
1396
1741
  async findMfaTotp(accountId: string): Promise<{
1397
1742
  readonly secretCiphertext: string;
1743
+ readonly keyId: string | null;
1398
1744
  readonly confirmedAt: number | null;
1399
1745
  } | null> {
1400
1746
  const rows = await this.#query<{
1401
1747
  secret_ciphertext: string;
1748
+ key_id: string | null;
1402
1749
  confirmed_at: number | bigint | string | null;
1403
1750
  }>(IDENTITY_TENANT_CONTEXT, {
1404
- text: `SELECT secret_ciphertext, confirmed_at FROM auth_mfa_totp
1751
+ text: `SELECT secret_ciphertext, key_id, confirmed_at FROM auth_mfa_totp
1405
1752
  WHERE account_id = $1`,
1406
1753
  parameters: [accountId],
1407
1754
  });
@@ -1409,11 +1756,24 @@ export class DatabaseAuthRepository implements AuthRepository {
1409
1756
  return row
1410
1757
  ? {
1411
1758
  secretCiphertext: row.secret_ciphertext,
1412
- confirmedAt: optionalInteger(row.confirmed_at),
1759
+ keyId: row.key_id,
1760
+ confirmedAt: optionalInteger(row.confirmed_at, 'confirmed_at'),
1413
1761
  }
1414
1762
  : null;
1415
1763
  }
1416
1764
 
1765
+ async hasConfirmedMfaTotp(accountId: string): Promise<boolean> {
1766
+ const rows = await this.#query<{ account_id: string }>(
1767
+ IDENTITY_TENANT_CONTEXT,
1768
+ {
1769
+ text: `SELECT account_id FROM auth_mfa_totp
1770
+ WHERE account_id = $1 AND confirmed_at IS NOT NULL`,
1771
+ parameters: [accountId],
1772
+ },
1773
+ );
1774
+ return rows.length > 0;
1775
+ }
1776
+
1417
1777
  async confirmMfaTotp(accountId: string, confirmedAt: number): Promise<void> {
1418
1778
  await this.#exec(IDENTITY_TENANT_CONTEXT, {
1419
1779
  text: 'UPDATE auth_mfa_totp SET confirmed_at = $1 WHERE account_id = $2',
@@ -1421,6 +1781,19 @@ export class DatabaseAuthRepository implements AuthRepository {
1421
1781
  });
1422
1782
  }
1423
1783
 
1784
+ async deleteMfaEnrolment(accountId: string): Promise<void> {
1785
+ await this.#tx(IDENTITY_TENANT_CONTEXT, 'write', async (transaction) => {
1786
+ await transaction.execute({
1787
+ text: 'DELETE FROM auth_mfa_totp WHERE account_id = $1',
1788
+ parameters: [accountId],
1789
+ });
1790
+ await transaction.execute({
1791
+ text: 'DELETE FROM auth_mfa_recovery_codes WHERE account_id = $1',
1792
+ parameters: [accountId],
1793
+ });
1794
+ });
1795
+ }
1796
+
1424
1797
  async replaceMfaRecoveryCodes(
1425
1798
  accountId: string,
1426
1799
  codeHashes: readonly string[],
@@ -1504,6 +1877,267 @@ export class DatabaseAuthRepository implements AuthRepository {
1504
1877
  return row ? { accountId: row.account_id, tenantId: row.tenant_id } : null;
1505
1878
  }
1506
1879
 
1880
+ /* A binding lives in one space: the platform space, whose rows carry no
1881
+ workspace and are read under the identity context exactly as before, or
1882
+ one workspace's space, read and written under that workspace. The policy
1883
+ on the table enforces the same split. */
1884
+ async findExternalIdentity(
1885
+ provider: string,
1886
+ subject: string,
1887
+ tenantId: string | null = null,
1888
+ ): Promise<string | null> {
1889
+ const rows = await this.#query<{ account_id: string }>(
1890
+ tenantId ?? IDENTITY_TENANT_CONTEXT,
1891
+ {
1892
+ text: `SELECT account_id FROM auth_external_identities
1893
+ WHERE provider = $1 AND subject = $2
1894
+ AND tenant_id IS NOT DISTINCT FROM $3`,
1895
+ parameters: [provider, subject, tenantId],
1896
+ },
1897
+ );
1898
+ return rows[0]?.account_id ?? null;
1899
+ }
1900
+
1901
+ async findExternalIdentitySubject(
1902
+ provider: string,
1903
+ accountId: string,
1904
+ tenantId: string | null = null,
1905
+ ): Promise<string | null> {
1906
+ const rows = await this.#query<{ subject: string }>(
1907
+ tenantId ?? IDENTITY_TENANT_CONTEXT,
1908
+ {
1909
+ text: `SELECT subject FROM auth_external_identities
1910
+ WHERE account_id = $1 AND provider = $2
1911
+ AND tenant_id IS NOT DISTINCT FROM $3`,
1912
+ parameters: [accountId, provider, tenantId],
1913
+ },
1914
+ );
1915
+ return rows[0]?.subject ?? null;
1916
+ }
1917
+
1918
+ async linkExternalIdentity(record: ExternalIdentityRecord): Promise<void> {
1919
+ const tenantId = record.tenantId ?? null;
1920
+ await this.#exec(tenantId ?? IDENTITY_TENANT_CONTEXT, {
1921
+ text: tenantId
1922
+ ? `INSERT INTO auth_external_identities
1923
+ (provider, subject, account_id, tenant_id, created_at, last_seen_at)
1924
+ VALUES ($1, $2, $3, $4, $5, $5)
1925
+ ON CONFLICT (tenant_id, provider, subject) WHERE tenant_id IS NOT NULL
1926
+ DO UPDATE SET last_seen_at = excluded.last_seen_at`
1927
+ : `INSERT INTO auth_external_identities
1928
+ (provider, subject, account_id, tenant_id, created_at, last_seen_at)
1929
+ VALUES ($1, $2, $3, $4, $5, $5)
1930
+ ON CONFLICT (provider, subject) WHERE tenant_id IS NULL
1931
+ DO UPDATE SET last_seen_at = excluded.last_seen_at`,
1932
+ parameters: [
1933
+ record.provider,
1934
+ record.subject,
1935
+ record.accountId,
1936
+ tenantId,
1937
+ record.now,
1938
+ ],
1939
+ });
1940
+ }
1941
+
1942
+ /* Keyset by (provider, subject), which the unique workspace index covers in
1943
+ that order, so a page is the rows it returns. The select list is the
1944
+ binding and nothing else: no token, ciphertext, fingerprint or reported
1945
+ address leaves this statement. A platform provider's binding carries no
1946
+ workspace, so the predicate never reaches one. */
1947
+ async listExternalIdentitiesPage(
1948
+ tenantId: string,
1949
+ after: { readonly provider: string; readonly subject: string },
1950
+ limit: number,
1951
+ ): Promise<readonly ExternalIdentityBinding[]> {
1952
+ const rows = await this.#query<ExternalIdentityBindingRow>(tenantId, {
1953
+ text: `SELECT account_id, provider, subject, created_at
1954
+ FROM auth_external_identities
1955
+ WHERE tenant_id = $1 AND (provider, subject) > ($2, $3)
1956
+ ORDER BY provider, subject
1957
+ LIMIT $4`,
1958
+ parameters: [tenantId, after.provider, after.subject, limit],
1959
+ });
1960
+ return rows.map((row) => ({
1961
+ accountId: row.account_id,
1962
+ provider: row.provider,
1963
+ subject: row.subject,
1964
+ linkedAt: integer(row.created_at, 'created_at'),
1965
+ }));
1966
+ }
1967
+
1968
+ /* A deleted provider takes its workspace's bindings with it; the accounts
1969
+ stay and keep every other way of signing in. */
1970
+ async deleteExternalIdentitiesOfProvider(
1971
+ tenantId: string,
1972
+ provider: string,
1973
+ ): Promise<void> {
1974
+ await this.#exec(tenantId, {
1975
+ text: `DELETE FROM auth_external_identities
1976
+ WHERE tenant_id = $1 AND provider = $2`,
1977
+ parameters: [tenantId, provider],
1978
+ });
1979
+ }
1980
+
1981
+ async listIdentityProviders(
1982
+ tenantId: string,
1983
+ ): Promise<readonly IdentityProviderRecord[]> {
1984
+ const rows = await this.#query<IdentityProviderRow>(tenantId, {
1985
+ text: `SELECT * FROM auth_identity_providers
1986
+ WHERE tenant_id = $1 ORDER BY key, id`,
1987
+ parameters: [tenantId],
1988
+ });
1989
+ return rows.map(fromIdentityProviderRow);
1990
+ }
1991
+
1992
+ async findIdentityProvider(
1993
+ tenantId: string,
1994
+ id: string,
1995
+ ): Promise<IdentityProviderRecord | null> {
1996
+ const rows = await this.#query<IdentityProviderRow>(tenantId, {
1997
+ text: 'SELECT * FROM auth_identity_providers WHERE tenant_id = $1 AND id = $2',
1998
+ parameters: [tenantId, id],
1999
+ });
2000
+ return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
2001
+ }
2002
+
2003
+ async findIdentityProviderByKey(
2004
+ tenantId: string,
2005
+ key: string,
2006
+ ): Promise<IdentityProviderRecord | null> {
2007
+ const rows = await this.#query<IdentityProviderRow>(tenantId, {
2008
+ text: 'SELECT * FROM auth_identity_providers WHERE tenant_id = $1 AND key = $2',
2009
+ parameters: [tenantId, key],
2010
+ });
2011
+ return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
2012
+ }
2013
+
2014
+ async createIdentityProvider(
2015
+ record: IdentityProviderRecord,
2016
+ ): Promise<IdentityProviderRecord> {
2017
+ try {
2018
+ const rows = await this.#tx(
2019
+ record.tenantId,
2020
+ 'write',
2021
+ async (transaction) => {
2022
+ const result = await transaction.query<IdentityProviderRow>({
2023
+ text: `INSERT INTO auth_identity_providers
2024
+ (id, tenant_id, key, label, issuer, authorization_endpoint,
2025
+ token_endpoint, user_info_endpoint, client_id,
2026
+ client_secret_ciphertext, client_secret_key_id,
2027
+ client_secret_fingerprint, scopes_json, jit_enabled,
2028
+ allowed_domains_json, jit_role, status, created_at, updated_at)
2029
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13,
2030
+ $14, $15, $16, $17, $18, $18)
2031
+ RETURNING *`,
2032
+ parameters: [
2033
+ record.id,
2034
+ record.tenantId,
2035
+ record.key,
2036
+ record.label,
2037
+ record.issuer,
2038
+ record.authorizationEndpoint,
2039
+ record.tokenEndpoint,
2040
+ record.userInfoEndpoint,
2041
+ record.clientId,
2042
+ record.secretCiphertext,
2043
+ record.secretKeyId,
2044
+ record.secretFingerprint,
2045
+ JSON.stringify(record.scopes),
2046
+ record.jitEnabled ? 1 : 0,
2047
+ JSON.stringify(record.allowedDomains),
2048
+ record.jitRole,
2049
+ record.status,
2050
+ record.createdAt,
2051
+ ],
2052
+ });
2053
+ return result.rows;
2054
+ },
2055
+ );
2056
+ return fromIdentityProviderRow(rows[0]!);
2057
+ } catch (error) {
2058
+ if (duplicate(error) === 'provider-key') {
2059
+ throw new DuplicateProviderKeyError();
2060
+ }
2061
+ throw error;
2062
+ }
2063
+ }
2064
+
2065
+ async updateIdentityProvider(
2066
+ tenantId: string,
2067
+ id: string,
2068
+ patch: IdentityProviderPatch,
2069
+ updatedAt: number,
2070
+ ): Promise<IdentityProviderRecord | null> {
2071
+ const rows = await this.#tx(tenantId, 'write', async (transaction) => {
2072
+ const result = await transaction.query<IdentityProviderRow>({
2073
+ text: `UPDATE auth_identity_providers
2074
+ SET label = $3, issuer = $4, authorization_endpoint = $5,
2075
+ token_endpoint = $6, user_info_endpoint = $7, client_id = $8,
2076
+ client_secret_ciphertext = $9, client_secret_key_id = $10,
2077
+ client_secret_fingerprint = $11, scopes_json = $12,
2078
+ jit_enabled = $13, allowed_domains_json = $14, jit_role = $15,
2079
+ status = $16, updated_at = $17
2080
+ WHERE tenant_id = $1 AND id = $2
2081
+ RETURNING *`,
2082
+ parameters: [
2083
+ tenantId,
2084
+ id,
2085
+ patch.label,
2086
+ patch.issuer,
2087
+ patch.authorizationEndpoint,
2088
+ patch.tokenEndpoint,
2089
+ patch.userInfoEndpoint,
2090
+ patch.clientId,
2091
+ patch.secretCiphertext,
2092
+ patch.secretKeyId,
2093
+ patch.secretFingerprint,
2094
+ JSON.stringify(patch.scopes),
2095
+ patch.jitEnabled ? 1 : 0,
2096
+ JSON.stringify(patch.allowedDomains),
2097
+ patch.jitRole,
2098
+ patch.status,
2099
+ updatedAt,
2100
+ ],
2101
+ });
2102
+ return result.rows;
2103
+ });
2104
+ return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
2105
+ }
2106
+
2107
+ async deleteIdentityProvider(tenantId: string, id: string): Promise<boolean> {
2108
+ const affected = await this.#exec(tenantId, {
2109
+ text: 'DELETE FROM auth_identity_providers WHERE tenant_id = $1 AND id = $2',
2110
+ parameters: [tenantId, id],
2111
+ });
2112
+ return affected > 0;
2113
+ }
2114
+
2115
+ async revokeMembershipApiTokens(
2116
+ tenantId: string,
2117
+ accountId: string,
2118
+ revokedAt: number,
2119
+ revokedBy: string,
2120
+ ): Promise<number> {
2121
+ return this.#exec(tenantId, {
2122
+ text: `UPDATE auth_api_tokens SET revoked_at = $1, revoked_by = $2
2123
+ WHERE tenant_id = $3 AND account_id = $4 AND revoked_at IS NULL`,
2124
+ parameters: [revokedAt, revokedBy, tenantId, accountId],
2125
+ });
2126
+ }
2127
+
2128
+ async setMembershipStatus(
2129
+ accountId: string,
2130
+ tenantId: string,
2131
+ status: MembershipStatus,
2132
+ ): Promise<boolean> {
2133
+ const affected = await this.#exec(tenantId, {
2134
+ text: `UPDATE auth_memberships SET status = $1
2135
+ WHERE account_id = $2 AND tenant_id = $3`,
2136
+ parameters: [status, accountId, tenantId],
2137
+ });
2138
+ return affected > 0;
2139
+ }
2140
+
1507
2141
  async findSignInFailure(
1508
2142
  normalizedEmail: string,
1509
2143
  ): Promise<SignInFailureRecord | null> {
@@ -1518,8 +2152,8 @@ export class DatabaseAuthRepository implements AuthRepository {
1518
2152
  const row = rows[0];
1519
2153
  return row
1520
2154
  ? {
1521
- failures: integer(row.failures),
1522
- lockedUntil: optionalInteger(row.locked_until),
2155
+ failures: integer(row.failures, 'failures'),
2156
+ lockedUntil: optionalInteger(row.locked_until, 'locked_until'),
1523
2157
  }
1524
2158
  : null;
1525
2159
  }
@@ -1550,12 +2184,13 @@ export class DatabaseAuthRepository implements AuthRepository {
1550
2184
  parameters: [normalizedEmail],
1551
2185
  });
1552
2186
  const previous = current.rows[0];
1553
- const failures = (previous ? integer(previous.failures) : 0) + 1;
2187
+ const failures =
2188
+ (previous ? integer(previous.failures, 'failures') : 0) + 1;
1554
2189
  const lockedUntil =
1555
2190
  failures >= lockThreshold
1556
2191
  ? now + lockMs
1557
2192
  : previous
1558
- ? optionalInteger(previous.locked_until)
2193
+ ? optionalInteger(previous.locked_until, 'locked_until')
1559
2194
  : null;
1560
2195
  await transaction.execute({
1561
2196
  text: `INSERT INTO auth_sign_in_failures
@@ -1693,21 +2328,23 @@ export class DatabaseAuthRepository implements AuthRepository {
1693
2328
  WHERE tenant_id = $1 AND role_id = $2`,
1694
2329
  parameters: [tenantId, roleId],
1695
2330
  });
1696
- return rows[0] ? integer(rows[0].total) : 0;
2331
+ return rows[0] ? integer(rows[0].total, 'total') : 0;
1697
2332
  }
1698
2333
 
1699
2334
  async appendAudit(record: AuditRecord): Promise<void> {
1700
2335
  await this.#exec(record.tenantId, {
1701
2336
  text: `INSERT INTO auth_audit
1702
2337
  (tenant_id, actor_account_id, actor_label, actor_kind, actor_run_id,
1703
- action, subject_type, subject_id, metadata_json, occurred_at)
1704
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
2338
+ configured_by_json, action, subject_type, subject_id, metadata_json,
2339
+ occurred_at)
2340
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
1705
2341
  parameters: [
1706
2342
  record.tenantId,
1707
2343
  record.actorAccountId,
1708
2344
  record.actorLabel,
1709
2345
  record.actorKind,
1710
2346
  record.actorRunId,
2347
+ record.configuredBy ? JSON.stringify(record.configuredBy) : null,
1711
2348
  record.action,
1712
2349
  record.subjectType,
1713
2350
  record.subjectId,
@@ -1725,6 +2362,16 @@ export class DatabaseAuthRepository implements AuthRepository {
1725
2362
  return `$${parameters.length}`;
1726
2363
  };
1727
2364
  if (query.action) conditions.push(`action = ${marker(query.action)}`);
2365
+ /* The window is inclusive and each end is independent, so a caller asks
2366
+ for a date range instead of seeding the cursor at its ceiling and
2367
+ watching every page for the floor. It composes with the filters and the
2368
+ keyset rather than replacing any of them. */
2369
+ if (query.from !== undefined && query.from !== null) {
2370
+ conditions.push(`occurred_at >= ${marker(query.from)}`);
2371
+ }
2372
+ if (query.to !== undefined && query.to !== null) {
2373
+ conditions.push(`occurred_at <= ${marker(query.to)}`);
2374
+ }
1728
2375
  if (query.actor) {
1729
2376
  conditions.push(
1730
2377
  `(actor_account_id = ${marker(query.actor)} OR actor_label = ${marker(query.actor)})`,
@@ -1744,20 +2391,155 @@ export class DatabaseAuthRepository implements AuthRepository {
1744
2391
  parameters,
1745
2392
  });
1746
2393
  return rows.map((row) => ({
1747
- id: integer(row.id),
2394
+ id: integer(row.id, 'id'),
1748
2395
  tenantId: row.tenant_id,
1749
2396
  actorAccountId: row.actor_account_id,
1750
2397
  actorLabel: row.actor_label,
1751
2398
  actorKind: row.actor_kind,
1752
2399
  actorRunId: row.actor_run_id,
2400
+ configuredBy: row.configured_by_json
2401
+ ? (JSON.parse(row.configured_by_json) as UserActor)
2402
+ : null,
2403
+ action: row.action,
2404
+ subjectType: row.subject_type,
2405
+ subjectId: row.subject_id,
2406
+ metadata: JSON.parse(row.metadata_json) as Record<string, unknown>,
2407
+ occurredAt: integer(row.occurred_at, 'occurred_at'),
2408
+ }));
2409
+ }
2410
+
2411
+ async exportSessionsPage(
2412
+ tenantId: string,
2413
+ afterId: string,
2414
+ limit: number,
2415
+ ): Promise<readonly SessionExportRecord[]> {
2416
+ const rows = await this.#query<SessionExportRow>(tenantId, {
2417
+ text: `SELECT id, tenant_id, account_id, created_at, expires_at,
2418
+ last_seen_at
2419
+ FROM auth_sessions
2420
+ WHERE tenant_id = $1 AND id > $2
2421
+ ORDER BY id LIMIT $3`,
2422
+ parameters: [tenantId, afterId, limit],
2423
+ });
2424
+ return rows.map((row) => ({
2425
+ id: row.id,
2426
+ tenantId: row.tenant_id,
2427
+ accountId: row.account_id,
2428
+ createdAt: integer(row.created_at, 'created_at'),
2429
+ expiresAt: integer(row.expires_at, 'expires_at'),
2430
+ lastSeenAt: integer(row.last_seen_at, 'last_seen_at'),
2431
+ }));
2432
+ }
2433
+
2434
+ async deleteSessionsExpiredBefore(
2435
+ tenantId: string,
2436
+ before: number,
2437
+ limit: number,
2438
+ ): Promise<number> {
2439
+ return this.#exec(tenantId, {
2440
+ text: `DELETE FROM auth_sessions WHERE token_hash IN (
2441
+ SELECT token_hash FROM auth_sessions
2442
+ WHERE tenant_id = $1 AND expires_at < $2
2443
+ ORDER BY expires_at LIMIT $3)`,
2444
+ parameters: [tenantId, before, limit],
2445
+ });
2446
+ }
2447
+
2448
+ async exportApiTokensPage(
2449
+ tenantId: string,
2450
+ afterId: string,
2451
+ limit: number,
2452
+ ): Promise<readonly ApiTokenRecord[]> {
2453
+ const rows = await this.#query<ApiTokenRow>(tenantId, {
2454
+ text: `SELECT * FROM auth_api_tokens
2455
+ WHERE tenant_id = $1 AND id > $2
2456
+ ORDER BY id LIMIT $3`,
2457
+ parameters: [tenantId, afterId, limit],
2458
+ });
2459
+ return rows.map(fromApiTokenRow);
2460
+ }
2461
+
2462
+ async deleteApiTokensRetiredBefore(
2463
+ tenantId: string,
2464
+ before: number,
2465
+ limit: number,
2466
+ ): Promise<number> {
2467
+ return this.#exec(tenantId, {
2468
+ text: `DELETE FROM auth_api_tokens WHERE id IN (
2469
+ SELECT id FROM auth_api_tokens
2470
+ WHERE tenant_id = $1
2471
+ AND (revoked_at < $2 OR expires_at < $2)
2472
+ ORDER BY id LIMIT $3)`,
2473
+ parameters: [tenantId, before, limit],
2474
+ });
2475
+ }
2476
+
2477
+ async exportAuditEventsPage(
2478
+ tenantId: string,
2479
+ afterId: number,
2480
+ limit: number,
2481
+ ): Promise<readonly AuditEvent[]> {
2482
+ const rows = await this.#query<AuditRow>(tenantId, {
2483
+ text: `SELECT * FROM auth_audit
2484
+ WHERE tenant_id = $1 AND id > $2
2485
+ ORDER BY id LIMIT $3`,
2486
+ parameters: [tenantId, afterId, limit],
2487
+ });
2488
+ return rows.map((row) => ({
2489
+ id: integer(row.id, 'id'),
2490
+ tenantId: row.tenant_id,
2491
+ actorAccountId: row.actor_account_id,
2492
+ actorLabel: row.actor_label,
1753
2493
  action: row.action,
1754
2494
  subjectType: row.subject_type,
1755
2495
  subjectId: row.subject_id,
1756
2496
  metadata: JSON.parse(row.metadata_json) as Record<string, unknown>,
1757
- occurredAt: integer(row.occurred_at),
2497
+ occurredAt: integer(row.occurred_at, 'occurred_at'),
1758
2498
  }));
1759
2499
  }
1760
2500
 
2501
+ async deleteAuditEventsBefore(
2502
+ tenantId: string,
2503
+ before: number,
2504
+ limit: number,
2505
+ ): Promise<number> {
2506
+ return this.#exec(tenantId, {
2507
+ text: `DELETE FROM auth_audit WHERE id IN (
2508
+ SELECT id FROM auth_audit
2509
+ WHERE tenant_id = $1 AND occurred_at < $2
2510
+ ORDER BY occurred_at LIMIT $3)`,
2511
+ parameters: [tenantId, before, limit],
2512
+ });
2513
+ }
2514
+
2515
+ async deleteMembershipSessionsOf(
2516
+ tenantId: string,
2517
+ accountId: string,
2518
+ limit: number,
2519
+ ): Promise<number> {
2520
+ return this.#exec(tenantId, {
2521
+ text: `DELETE FROM auth_sessions WHERE token_hash IN (
2522
+ SELECT token_hash FROM auth_sessions
2523
+ WHERE tenant_id = $1 AND account_id = $2
2524
+ ORDER BY token_hash LIMIT $3)`,
2525
+ parameters: [tenantId, accountId, limit],
2526
+ });
2527
+ }
2528
+
2529
+ async deleteMembershipApiTokensOf(
2530
+ tenantId: string,
2531
+ accountId: string,
2532
+ limit: number,
2533
+ ): Promise<number> {
2534
+ return this.#exec(tenantId, {
2535
+ text: `DELETE FROM auth_api_tokens WHERE id IN (
2536
+ SELECT id FROM auth_api_tokens
2537
+ WHERE tenant_id = $1 AND account_id = $2
2538
+ ORDER BY id LIMIT $3)`,
2539
+ parameters: [tenantId, accountId, limit],
2540
+ });
2541
+ }
2542
+
1761
2543
  async loadSettings(
1762
2544
  tenantId: string,
1763
2545
  moduleId: string,