@flowdular/sdk 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai/agents/sandbox/business-manager.md +5 -1
- package/.ai/blueprints/author-spec/README.md +1 -1
- package/.ai/blueprints/author-spec/spec-requirements.yaml +44 -0
- package/.ai/blueprints/author-spec/steps.yaml +5 -5
- package/.ai/blueprints/author-spec/templates/module.yaml +99 -3
- package/.ai/blueprints/edit-module/gates.yaml +4 -0
- package/.ai/blueprints/edit-module/required-files.yaml +9 -0
- package/.ai/blueprints/new-module/gates.yaml +4 -0
- package/.ai/blueprints/new-module/spec-requirements.yaml +2 -2
- package/.ai/blueprints/release/gates.yaml +4 -0
- package/.ai/platform-capabilities.md +128 -0
- package/.ai/policies/capabilities.yaml +100 -0
- package/.ai/policies/path-ownership.yaml +5 -2
- package/.ai/policies/task-budgets.yaml +5 -3
- package/.ai/references/catalog/module.json +4 -4
- package/.ai/references/catalog/package.json +2 -2
- package/.ai/references/catalog/spec/module.yaml +5 -3
- package/.ai/references/catalog/src/platform.ts +2 -0
- package/.ai/references/catalog/src/services/catalog-service.ts +89 -1
- package/.ai/references/catalog/src/services/data-classes.ts +47 -0
- package/.ai/references/catalog/src/services/database-repository.ts +98 -1
- package/.ai/references/catalog/src/services/repository.ts +22 -1
- package/.ai/references/catalog/tests/data-classes.test.ts +157 -0
- package/.ai/references/catalog.provenance.json +12 -10
- package/.ai/skills/README.md +10 -0
- package/.ai/skills/agent-tool-design/SKILL.md +1 -2
- package/.ai/skills/auth-security-review/SKILL.md +1 -1
- package/.ai/skills/business-agent-design/SKILL.md +0 -1
- package/.ai/skills/deploy-operate/SKILL.md +114 -0
- package/.ai/skills/module-new/SKILL.md +29 -3
- package/.ai/skills/module-update/SKILL.md +9 -3
- package/.ai/skills/perf-audit/SKILL.md +0 -1
- package/.ai/skills/release-eject-pr/SKILL.md +0 -1
- package/.ai/skills/spec-interview/SKILL.md +120 -0
- package/.ai/skills/test-hardening/SKILL.md +1 -0
- package/.ai/skills/ux-design/SKILL.md +34 -3
- package/.ai/skills/variables/SKILL.md +0 -2
- package/.ai/skills/workflow-development/SKILL.md +0 -1
- package/AGENTS.md +4 -0
- package/docs/agent-contract.md +2 -2
- package/docs/design-system.md +185 -31
- package/modules/access/migrations/0001_access_core.down.sql +1 -0
- package/modules/access/migrations/0001_access_core.up.sql +28 -0
- package/modules/access/migrations/README.md +3 -0
- package/modules/access/module.json +33 -0
- package/modules/access/package.json +43 -0
- package/modules/access/spec/module.yaml +225 -0
- package/modules/access/src/acl/permissions.ts +6 -0
- package/modules/access/src/api/endpoints.ts +244 -0
- package/modules/access/src/client/ActivityView.tsrx +245 -0
- package/modules/access/src/client/AttestationsView.tsrx +176 -0
- package/modules/access/src/client/ReviewView.tsrx +586 -0
- package/modules/access/src/client/api.ts +132 -0
- package/modules/access/src/client/contribution.tsrx +41 -0
- package/modules/access/src/client/index.ts +41 -0
- package/modules/access/src/client/navigation.ts +57 -0
- package/modules/access/src/client/presentation.ts +69 -0
- package/modules/access/src/client/state.ts +72 -0
- package/modules/access/src/domain/types.ts +189 -0
- package/modules/access/src/index.ts +38 -0
- package/modules/access/src/platform.ts +60 -0
- package/modules/access/src/server/index.ts +42 -0
- package/modules/access/src/server/runtime.ts +105 -0
- package/modules/access/src/services/access-service.ts +298 -0
- package/modules/access/src/services/audit-window.ts +78 -0
- package/modules/access/src/services/auth-directory.ts +72 -0
- package/modules/access/src/services/changes.ts +91 -0
- package/modules/access/src/services/data-classes.ts +28 -0
- package/modules/access/src/services/database-repository.ts +185 -0
- package/modules/access/src/services/directory.ts +102 -0
- package/modules/access/src/services/index.ts +33 -0
- package/modules/access/src/services/list-exports.ts +150 -0
- package/modules/access/src/services/migration.ts +54 -0
- package/modules/access/src/services/repository.ts +30 -0
- package/modules/access/src/services/review.ts +178 -0
- package/modules/access/translations/en.json +155 -0
- package/modules/access/translations/pl.json +155 -0
- package/modules/agents/migrations/0022_credential_rotation_inventory.down.sql +2 -0
- package/modules/agents/migrations/0022_credential_rotation_inventory.up.sql +7 -0
- package/modules/agents/migrations/0023_agents_retention_indexes.down.sql +2 -0
- package/modules/agents/migrations/0023_agents_retention_indexes.up.sql +9 -0
- package/modules/agents/migrations/0024_agent_meter_refusals.down.sql +3 -0
- package/modules/agents/migrations/0024_agent_meter_refusals.up.sql +22 -0
- package/modules/agents/migrations/0025_agent_list_indexes.down.sql +3 -0
- package/modules/agents/migrations/0025_agent_list_indexes.up.sql +11 -0
- package/modules/agents/module.json +30 -4
- package/modules/agents/package.json +2 -1
- package/modules/agents/spec/module.yaml +63 -5
- package/modules/agents/src/api/endpoints.ts +291 -33
- package/modules/agents/src/cli/commands.json +11 -0
- package/modules/agents/src/cli/index.ts +45 -0
- package/modules/agents/src/client/AgentDashboardWidget.tsrx +19 -11
- package/modules/agents/src/client/AgentDefinitionList.tsrx +53 -10
- package/modules/agents/src/client/AgentPlaygroundView.tsrx +4 -4
- package/modules/agents/src/client/AgentProceduresView.tsrx +2 -2
- package/modules/agents/src/client/AgentRunsView.tsrx +143 -40
- package/modules/agents/src/client/AgentsView.tsrx +109 -33
- package/modules/agents/src/client/api.ts +99 -21
- package/modules/agents/src/client/state.ts +97 -0
- package/modules/agents/src/domain/types.ts +44 -4
- package/modules/agents/src/platform.ts +42 -0
- package/modules/agents/src/server/action-execution.ts +201 -106
- package/modules/agents/src/server/runtime.ts +32 -0
- package/modules/agents/src/services/agent-service.ts +158 -30
- package/modules/agents/src/services/credential-rotation.ts +185 -0
- package/modules/agents/src/services/credential-vault.ts +71 -46
- package/modules/agents/src/services/data-classes.ts +176 -0
- package/modules/agents/src/services/database-repository.ts +393 -101
- package/modules/agents/src/services/metering.ts +111 -0
- package/modules/agents/src/services/migration.ts +109 -0
- package/modules/agents/src/services/module-agent-preflight.ts +17 -9
- package/modules/agents/src/services/notifications.ts +98 -0
- package/modules/agents/src/services/provider-repository.ts +9 -18
- package/modules/agents/src/services/provider-service.ts +1 -9
- package/modules/agents/src/services/reports.ts +107 -0
- package/modules/agents/src/services/repository.ts +86 -6
- package/modules/agents/src/services/usage-service.ts +2 -0
- package/modules/agents/src/services/worker.ts +114 -4
- package/modules/agents/src/settings.ts +14 -3
- package/modules/agents/translations/en.json +14 -4
- package/modules/agents/translations/pl.json +14 -4
- package/modules/approvals/migrations/0001_approvals_core.down.sql +3 -0
- package/modules/approvals/migrations/0001_approvals_core.up.sql +78 -0
- package/modules/approvals/migrations/0002_approvals_expiry_routing_role.down.sql +2 -0
- package/modules/approvals/migrations/0002_approvals_expiry_routing_role.up.sql +19 -0
- package/modules/approvals/migrations/0003_approvals_retention_indexes.down.sql +6 -0
- package/modules/approvals/migrations/0003_approvals_retention_indexes.up.sql +14 -0
- package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.down.sql +5 -0
- package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.up.sql +12 -0
- package/modules/approvals/migrations/README.md +3 -0
- package/modules/approvals/module.json +34 -0
- package/modules/approvals/package.json +43 -0
- package/modules/approvals/spec/module.yaml +318 -0
- package/modules/approvals/src/acl/permissions.ts +7 -0
- package/modules/approvals/src/api/endpoints.ts +457 -0
- package/modules/approvals/src/client/ApprovalRequestDrawer.tsrx +138 -0
- package/modules/approvals/src/client/ApprovalsView.tsrx +562 -0
- package/modules/approvals/src/client/PendingCountWidget.tsrx +52 -0
- package/modules/approvals/src/client/api.ts +203 -0
- package/modules/approvals/src/client/approvals.css +51 -0
- package/modules/approvals/src/client/contribution.tsrx +44 -0
- package/modules/approvals/src/client/index.ts +26 -0
- package/modules/approvals/src/client/navigation.ts +39 -0
- package/modules/approvals/src/client/pending-count.ts +26 -0
- package/modules/approvals/src/client/presentation.ts +52 -0
- package/modules/approvals/src/client/state.ts +141 -0
- package/modules/approvals/src/domain/capability.ts +95 -0
- package/modules/approvals/src/domain/types.ts +154 -0
- package/modules/approvals/src/index.ts +52 -0
- package/modules/approvals/src/platform.ts +128 -0
- package/modules/approvals/src/server/index.ts +42 -0
- package/modules/approvals/src/server/runtime.ts +181 -0
- package/modules/approvals/src/services/approvals-service.ts +557 -0
- package/modules/approvals/src/services/callbacks.ts +75 -0
- package/modules/approvals/src/services/data-classes.ts +167 -0
- package/modules/approvals/src/services/database-repository.ts +724 -0
- package/modules/approvals/src/services/eligibility.ts +121 -0
- package/modules/approvals/src/services/expiry-runner.ts +81 -0
- package/modules/approvals/src/services/index.ts +31 -0
- package/modules/approvals/src/services/migration.ts +222 -0
- package/modules/approvals/src/services/notifications.ts +95 -0
- package/modules/approvals/src/services/repository.ts +161 -0
- package/modules/approvals/src/services/service-error.ts +70 -0
- package/modules/approvals/src/settings.ts +102 -0
- package/modules/approvals/translations/en.json +104 -0
- package/modules/approvals/translations/pl.json +104 -0
- package/modules/audit/migrations/0001_audit_core.down.sql +8 -0
- package/modules/audit/migrations/0001_audit_core.up.sql +107 -0
- package/modules/audit/migrations/0002_audit_sweep_routing_role.down.sql +2 -0
- package/modules/audit/migrations/0002_audit_sweep_routing_role.up.sql +21 -0
- package/modules/audit/migrations/0003_audit_export_request.down.sql +12 -0
- package/modules/audit/migrations/0003_audit_export_request.up.sql +40 -0
- package/modules/audit/migrations/0004_audit_hold_seal_erasure.down.sql +17 -0
- package/modules/audit/migrations/0004_audit_hold_seal_erasure.up.sql +192 -0
- package/modules/audit/migrations/0005_audit_event_format.down.sql +17 -0
- package/modules/audit/migrations/0005_audit_event_format.up.sql +60 -0
- package/modules/audit/migrations/0006_audit_own_class_walks.down.sql +3 -0
- package/modules/audit/migrations/0006_audit_own_class_walks.up.sql +14 -0
- package/modules/audit/migrations/README.md +3 -0
- package/modules/audit/module.json +32 -0
- package/modules/audit/package.json +49 -0
- package/modules/audit/spec/module.yaml +809 -0
- package/modules/audit/src/acl/permissions.ts +7 -0
- package/modules/audit/src/api/endpoints.ts +315 -0
- package/modules/audit/src/cli/commands.json +95 -0
- package/modules/audit/src/cli/index.ts +658 -0
- package/modules/audit/src/client/DataClassesView.tsrx +420 -0
- package/modules/audit/src/client/ExportsView.tsrx +256 -0
- package/modules/audit/src/client/HoldsView.tsrx +604 -0
- package/modules/audit/src/client/SweepsView.tsrx +251 -0
- package/modules/audit/src/client/api.ts +185 -0
- package/modules/audit/src/client/contribution.tsrx +52 -0
- package/modules/audit/src/client/index.ts +23 -0
- package/modules/audit/src/client/navigation.ts +74 -0
- package/modules/audit/src/client/presentation.ts +105 -0
- package/modules/audit/src/client/state.ts +86 -0
- package/modules/audit/src/domain/data-classes.ts +16 -0
- package/modules/audit/src/domain/types.ts +339 -0
- package/modules/audit/src/index.ts +76 -0
- package/modules/audit/src/platform.ts +44 -0
- package/modules/audit/src/server/index.ts +177 -0
- package/modules/audit/src/server/runtime.ts +303 -0
- package/modules/audit/src/services/anchor-key.ts +143 -0
- package/modules/audit/src/services/anchor-rotation.ts +147 -0
- package/modules/audit/src/services/audit-runners.ts +278 -0
- package/modules/audit/src/services/backup-guard.ts +95 -0
- package/modules/audit/src/services/database-repository.ts +1946 -0
- package/modules/audit/src/services/declared-classes.ts +64 -0
- package/modules/audit/src/services/erasure-port.ts +126 -0
- package/modules/audit/src/services/erasure-service.ts +770 -0
- package/modules/audit/src/services/export-directory.ts +106 -0
- package/modules/audit/src/services/export-service.ts +714 -0
- package/modules/audit/src/services/hold-service.ts +295 -0
- package/modules/audit/src/services/index.ts +17 -0
- package/modules/audit/src/services/migration.ts +658 -0
- package/modules/audit/src/services/own-classes.ts +318 -0
- package/modules/audit/src/services/platform-version.ts +23 -0
- package/modules/audit/src/services/repository.ts +495 -0
- package/modules/audit/src/services/retention-service.ts +191 -0
- package/modules/audit/src/services/seal-service.ts +769 -0
- package/modules/audit/src/services/service-error.ts +61 -0
- package/modules/audit/src/services/subject-keys.ts +90 -0
- package/modules/audit/src/services/sweep-service.ts +298 -0
- package/modules/audit/src/services/zip.ts +217 -0
- package/modules/audit/src/settings.ts +69 -0
- package/modules/audit/translations/en.json +210 -0
- package/modules/audit/translations/pl.json +210 -0
- package/modules/auth/README.md +77 -29
- package/modules/auth/migrations/0016_external_identities.up.sql +16 -0
- package/modules/auth/migrations/0017_mfa_key_id.up.sql +8 -0
- package/modules/auth/migrations/0018_retire_bundled_module_scopes.up.sql +29 -0
- package/modules/auth/migrations/0019_notifications_member_scopes.up.sql +44 -0
- package/modules/auth/migrations/0020_identity_providers.up.sql +61 -0
- package/modules/auth/migrations/0021_membership_status.up.sql +19 -0
- package/modules/auth/migrations/0022_auth_provider_scopes.up.sql +45 -0
- package/modules/auth/migrations/0023_enterprise_module_scopes.down.sql +19 -0
- package/modules/auth/migrations/0023_enterprise_module_scopes.up.sql +71 -0
- package/modules/auth/migrations/0024_approvals_member_scopes.down.sql +22 -0
- package/modules/auth/migrations/0024_approvals_member_scopes.up.sql +48 -0
- package/modules/auth/migrations/0025_audit_holds_owner_scope.down.sql +21 -0
- package/modules/auth/migrations/0025_audit_holds_owner_scope.up.sql +47 -0
- package/modules/auth/migrations/0026_auth_export_keyset_indexes.down.sql +6 -0
- package/modules/auth/migrations/0026_auth_export_keyset_indexes.up.sql +15 -0
- package/modules/auth/migrations/0027_workflow_automation_profile_scopes.down.sql +34 -0
- package/modules/auth/migrations/0027_workflow_automation_profile_scopes.up.sql +72 -0
- package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.down.sql +6 -0
- package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.up.sql +21 -0
- package/modules/auth/migrations/0029_reports_exports_access_scopes.down.sql +22 -0
- package/modules/auth/migrations/0029_reports_exports_access_scopes.up.sql +56 -0
- package/modules/auth/migrations/0030_auth_membership_keyset_index.down.sql +4 -0
- package/modules/auth/migrations/0030_auth_membership_keyset_index.up.sql +10 -0
- package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.down.sql +4 -0
- package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.up.sql +31 -0
- package/modules/auth/migrations/0032_audit_actor_service.down.sql +9 -0
- package/modules/auth/migrations/0032_audit_actor_service.up.sql +10 -0
- package/modules/auth/migrations/0033_auth_account_keyset_indexes.down.sql +5 -0
- package/modules/auth/migrations/0033_auth_account_keyset_indexes.up.sql +23 -0
- package/modules/auth/module.json +3 -3
- package/modules/auth/package.json +2 -1
- package/modules/auth/spec/module.yaml +372 -3
- package/modules/auth/src/acl/scopes.ts +50 -6
- package/modules/auth/src/cli/commands.json +11 -0
- package/modules/auth/src/cli/greenfield.ts +2 -2
- package/modules/auth/src/cli/index.ts +14 -0
- package/modules/auth/src/cli/secrets.ts +114 -0
- package/modules/auth/src/client/AuthenticationCore.tsrx +61 -8
- package/modules/auth/src/client/api.ts +72 -6
- package/modules/auth/src/client/contribution.tsrx +23 -0
- package/modules/auth/src/client/index.ts +6 -0
- package/modules/auth/src/client/providers/IdentityProviderForm.tsrx +191 -0
- package/modules/auth/src/client/providers/IdentityProvidersView.tsrx +358 -0
- package/modules/auth/src/client/providers/api.ts +128 -0
- package/modules/auth/src/client/providers/provider-columns.tsrx +60 -0
- package/modules/auth/src/client/providers/state.ts +35 -0
- package/modules/auth/src/client/security/SecurityView.tsrx +9 -1
- package/modules/auth/src/client/state.ts +15 -1
- package/modules/auth/src/domain/types.ts +55 -0
- package/modules/auth/src/index.ts +11 -1
- package/modules/auth/src/middleware/authentication.ts +4 -0
- package/modules/auth/src/server/endpoints.ts +467 -171
- package/modules/auth/src/server/http.ts +3 -2
- package/modules/auth/src/server/index.ts +22 -1
- package/modules/auth/src/server/membership-endpoints.ts +62 -0
- package/modules/auth/src/server/mfa-enforcement.ts +156 -0
- package/modules/auth/src/server/oidc.ts +389 -0
- package/modules/auth/src/server/provider-endpoints.ts +246 -0
- package/modules/auth/src/server/provider-host.ts +77 -0
- package/modules/auth/src/server/runtime.ts +231 -55
- package/modules/auth/src/services/auth-service-error.ts +7 -2
- package/modules/auth/src/services/auth-service.ts +948 -51
- package/modules/auth/src/services/data-classes.ts +236 -0
- package/modules/auth/src/services/database-repository.ts +853 -71
- package/modules/auth/src/services/identity-provider-service.ts +571 -0
- package/modules/auth/src/services/mail-delivery.ts +16 -16
- package/modules/auth/src/services/mail-message.ts +54 -0
- package/modules/auth/src/services/mail-port.ts +24 -0
- package/modules/auth/src/services/mail-smtp.ts +85 -0
- package/modules/auth/src/services/mfa-rotation.ts +144 -0
- package/modules/auth/src/services/migration.ts +810 -0
- package/modules/auth/src/services/password.ts +14 -0
- package/modules/auth/src/services/provider-secret-rotation.ts +169 -0
- package/modules/auth/src/services/provider-secrets.ts +136 -0
- package/modules/auth/src/services/repository.ts +321 -5
- package/modules/auth/src/services/session-sweep-runner.ts +73 -0
- package/modules/auth/src/services/settings-store.ts +10 -83
- package/modules/auth/src/services/totp.ts +88 -62
- package/modules/auth/src/services/validation.ts +194 -7
- package/modules/auth/src/settings.ts +18 -0
- package/modules/auth/translations/en.json +84 -1
- package/modules/auth/translations/pl.json +84 -1
- package/modules/automations/migrations/0005_secret_rotation_inventory.down.sql +1 -0
- package/modules/automations/migrations/0005_secret_rotation_inventory.up.sql +6 -0
- package/modules/automations/migrations/0006_automations_list_sort_indexes.down.sql +4 -0
- package/modules/automations/migrations/0006_automations_list_sort_indexes.up.sql +11 -0
- package/modules/automations/module.json +24 -6
- package/modules/automations/package.json +4 -1
- package/modules/automations/spec/module.yaml +81 -7
- package/modules/automations/src/api/endpoints.ts +202 -28
- package/modules/automations/src/cli/commands.json +17 -0
- package/modules/automations/src/cli/index.ts +114 -0
- package/modules/automations/src/client/AutomationScheduleForm.tsrx +77 -19
- package/modules/automations/src/client/AutomationSchedulesView.tsrx +144 -40
- package/modules/automations/src/client/AutomationTriggersView.tsrx +131 -32
- package/modules/automations/src/client/api.ts +62 -19
- package/modules/automations/src/client/presentation.ts +20 -2
- package/modules/automations/src/client/state.ts +84 -0
- package/modules/automations/src/domain/cadence.ts +99 -3
- package/modules/automations/src/domain/cron.ts +366 -0
- package/modules/automations/src/domain/time-zone.ts +61 -0
- package/modules/automations/src/platform.ts +38 -4
- package/modules/automations/src/server/index.ts +11 -0
- package/modules/automations/src/server/runtime.ts +46 -22
- package/modules/{automations-workflows-integration/src/server/adapter.ts → automations/src/server/workflow-target.ts} +13 -14
- package/modules/{automations-workflows-integration/src/server/tools.ts → automations/src/server/workflow-tools.ts} +0 -0
- package/modules/automations/src/services/data-classes.ts +62 -0
- package/modules/automations/src/services/database-repository.ts +152 -38
- package/modules/automations/src/services/migration.ts +119 -9
- package/modules/automations/src/services/repository.ts +52 -1
- package/modules/automations/src/services/schedule-runner.ts +84 -0
- package/modules/automations/src/services/schedule-service.ts +152 -26
- package/modules/automations/src/services/secret-rotation.ts +176 -0
- package/modules/automations/src/services/secret-vault.ts +68 -41
- package/modules/automations/src/services/trigger-service.ts +13 -10
- package/modules/automations/translations/en.json +15 -2
- package/modules/automations/translations/pl.json +15 -2
- package/modules/connectors/migrations/0001_connectors_core.down.sql +3 -0
- package/modules/connectors/migrations/0001_connectors_core.up.sql +72 -0
- package/modules/connectors/migrations/0002_connectors_call_keys.down.sql +3 -0
- package/modules/connectors/migrations/0002_connectors_call_keys.up.sql +18 -0
- package/modules/connectors/migrations/README.md +3 -0
- package/modules/connectors/module.json +28 -0
- package/modules/{automations-workflows-integration → connectors}/package.json +27 -26
- package/modules/connectors/spec/module.yaml +447 -0
- package/modules/connectors/src/acl/permissions.ts +6 -0
- package/modules/connectors/src/agent/tools.ts +180 -0
- package/modules/connectors/src/api/endpoints.ts +572 -0
- package/modules/connectors/src/client/ConnectorCallsView.tsrx +358 -0
- package/modules/connectors/src/client/ConnectorForm.tsrx +424 -0
- package/modules/connectors/src/client/ConnectorsView.tsrx +711 -0
- package/modules/connectors/src/client/api.ts +281 -0
- package/modules/connectors/src/client/consent.ts +48 -0
- package/modules/connectors/src/client/contribution.tsrx +37 -0
- package/modules/connectors/src/client/index.ts +22 -0
- package/modules/connectors/src/client/navigation.ts +42 -0
- package/modules/connectors/src/client/presentation.ts +38 -0
- package/modules/connectors/src/client/state.ts +175 -0
- package/modules/connectors/src/domain/calls.ts +70 -0
- package/modules/connectors/src/domain/definitions.ts +190 -0
- package/modules/connectors/src/domain/http-json.ts +68 -0
- package/modules/connectors/src/domain/types.ts +185 -0
- package/modules/connectors/src/index.ts +64 -0
- package/modules/connectors/src/platform.ts +56 -0
- package/modules/connectors/src/server/index.ts +66 -0
- package/modules/connectors/src/server/runtime.ts +163 -0
- package/modules/connectors/src/services/call-service.ts +903 -0
- package/modules/connectors/src/services/connectors-service.ts +653 -0
- package/modules/connectors/src/services/credential-vault.ts +188 -0
- package/modules/connectors/src/services/data-classes.ts +158 -0
- package/modules/connectors/src/services/database-repository.ts +897 -0
- package/modules/connectors/src/services/egress.ts +261 -0
- package/modules/connectors/src/services/index.ts +19 -0
- package/modules/connectors/src/services/migration.ts +166 -0
- package/modules/connectors/src/services/repository.ts +175 -0
- package/modules/connectors/src/services/service-error.ts +46 -0
- package/modules/connectors/src/settings.ts +69 -0
- package/modules/connectors/translations/en.json +191 -0
- package/modules/connectors/translations/pl.json +191 -0
- package/modules/directory/migrations/0001_directory_core.down.sql +5 -0
- package/modules/directory/migrations/0001_directory_core.up.sql +111 -0
- package/modules/directory/migrations/README.md +3 -0
- package/modules/directory/module.json +27 -0
- package/modules/directory/package.json +46 -0
- package/modules/directory/spec/module.yaml +351 -0
- package/modules/directory/src/acl/permissions.ts +7 -0
- package/modules/directory/src/api/endpoints.ts +478 -0
- package/modules/directory/src/api/scim-endpoints.ts +516 -0
- package/modules/directory/src/client/GroupMappingsView.tsrx +430 -0
- package/modules/directory/src/client/ProvisioningLogView.tsrx +248 -0
- package/modules/directory/src/client/ScimTokensView.tsrx +587 -0
- package/modules/directory/src/client/api.ts +222 -0
- package/modules/directory/src/client/contribution.tsrx +46 -0
- package/modules/directory/src/client/index.ts +22 -0
- package/modules/directory/src/client/navigation.ts +57 -0
- package/modules/directory/src/client/presentation.ts +143 -0
- package/modules/directory/src/client/state.ts +173 -0
- package/modules/directory/src/domain/scim.ts +346 -0
- package/modules/directory/src/domain/types.ts +166 -0
- package/modules/directory/src/index.ts +44 -0
- package/modules/directory/src/platform.ts +43 -0
- package/modules/directory/src/server/index.ts +17 -0
- package/modules/directory/src/server/runtime.ts +124 -0
- package/modules/directory/src/services/auth-port.ts +127 -0
- package/modules/directory/src/services/data-classes.ts +41 -0
- package/modules/directory/src/services/database-repository.ts +1067 -0
- package/modules/directory/src/services/directory-service.ts +311 -0
- package/modules/directory/src/services/index.ts +43 -0
- package/modules/directory/src/services/migration.ts +146 -0
- package/modules/directory/src/services/provisioning-service.ts +1420 -0
- package/modules/directory/src/services/rate-limiter.ts +103 -0
- package/modules/directory/src/services/repository.ts +208 -0
- package/modules/directory/src/services/service-error.ts +42 -0
- package/modules/directory/src/services/token-service.ts +215 -0
- package/modules/directory/src/settings.ts +86 -0
- package/modules/directory/translations/en.json +185 -0
- package/modules/directory/translations/pl.json +185 -0
- package/modules/documents/migrations/0001_documents_core.down.sql +1 -0
- package/modules/documents/migrations/0001_documents_core.up.sql +35 -0
- package/modules/documents/migrations/0002_documents_files_page.down.sql +1 -0
- package/modules/documents/migrations/0002_documents_files_page.up.sql +6 -0
- package/modules/documents/migrations/README.md +3 -0
- package/modules/documents/module.json +28 -0
- package/modules/documents/package.json +49 -0
- package/modules/documents/spec/module.yaml +247 -0
- package/modules/documents/src/acl/permissions.ts +6 -0
- package/modules/documents/src/api/endpoints.ts +401 -0
- package/modules/documents/src/client/DocumentUploader.tsrx +148 -0
- package/modules/documents/src/client/DocumentsView.tsrx +540 -0
- package/modules/documents/src/client/api.ts +209 -0
- package/modules/documents/src/client/contribution.tsrx +29 -0
- package/modules/documents/src/client/download.ts +70 -0
- package/modules/documents/src/client/index.ts +22 -0
- package/modules/documents/src/client/navigation.ts +25 -0
- package/modules/documents/src/client/presentation.ts +110 -0
- package/modules/documents/src/client/state.ts +117 -0
- package/modules/documents/src/domain/attachments.ts +84 -0
- package/modules/documents/src/domain/types.ts +80 -0
- package/modules/documents/src/index.ts +46 -0
- package/modules/documents/src/platform.ts +61 -0
- package/modules/documents/src/server/index.ts +30 -0
- package/modules/documents/src/server/runtime.ts +107 -0
- package/modules/documents/src/services/attachments.ts +39 -0
- package/modules/documents/src/services/data-classes.ts +25 -0
- package/modules/documents/src/services/database-repository.ts +259 -0
- package/modules/documents/src/services/documents-service.ts +586 -0
- package/modules/documents/src/services/index.ts +18 -0
- package/modules/documents/src/services/migration.ts +82 -0
- package/modules/documents/src/services/repository.ts +48 -0
- package/modules/documents/src/settings.ts +107 -0
- package/modules/documents/translations/en.json +111 -0
- package/modules/documents/translations/pl.json +111 -0
- package/modules/exports/migrations/0001_exports_core.down.sql +2 -0
- package/modules/exports/migrations/0001_exports_core.up.sql +28 -0
- package/modules/exports/migrations/0002_exports_job_routing_role.down.sql +4 -0
- package/modules/exports/migrations/0002_exports_job_routing_role.up.sql +21 -0
- package/modules/exports/migrations/README.md +3 -0
- package/modules/exports/module.json +28 -0
- package/modules/exports/package.json +44 -0
- package/modules/exports/spec/module.yaml +229 -0
- package/modules/exports/src/acl/permissions.ts +6 -0
- package/modules/exports/src/api/endpoints.ts +211 -0
- package/modules/exports/src/client/ExportsView.tsrx +327 -0
- package/modules/exports/src/client/api.ts +133 -0
- package/modules/exports/src/client/contribution.tsrx +31 -0
- package/modules/exports/src/client/index.ts +20 -0
- package/modules/exports/src/client/navigation.ts +25 -0
- package/modules/exports/src/client/presentation.ts +53 -0
- package/modules/exports/src/client/state.ts +56 -0
- package/modules/exports/src/domain/lists.ts +29 -0
- package/modules/exports/src/domain/types.ts +120 -0
- package/modules/exports/src/index.ts +41 -0
- package/modules/exports/src/platform.ts +80 -0
- package/modules/exports/src/server/index.ts +51 -0
- package/modules/exports/src/server/runtime.ts +160 -0
- package/modules/exports/src/services/data-classes.ts +56 -0
- package/modules/exports/src/services/database-repository.ts +405 -0
- package/modules/exports/src/services/export-runner.ts +123 -0
- package/modules/exports/src/services/export-service.ts +356 -0
- package/modules/exports/src/services/index.ts +6 -0
- package/modules/exports/src/services/list-registry.ts +105 -0
- package/modules/exports/src/services/migration.ts +89 -0
- package/modules/exports/src/services/repository.ts +97 -0
- package/modules/exports/src/settings.ts +70 -0
- package/modules/exports/translations/en.json +69 -0
- package/modules/exports/translations/pl.json +69 -0
- package/modules/import/migrations/0001_import_core.down.sql +6 -0
- package/modules/import/migrations/0001_import_core.up.sql +61 -0
- package/modules/import/migrations/0002_import_job_routing_role.down.sql +4 -0
- package/modules/import/migrations/0002_import_job_routing_role.up.sql +20 -0
- package/modules/import/migrations/0003_import_jobs_export_index.down.sql +1 -0
- package/modules/import/migrations/0003_import_jobs_export_index.up.sql +7 -0
- package/modules/import/migrations/0004_import_jobs_traceparent.down.sql +1 -0
- package/modules/import/migrations/0004_import_jobs_traceparent.up.sql +7 -0
- package/modules/import/migrations/README.md +3 -0
- package/modules/import/module.json +37 -0
- package/modules/import/package.json +50 -0
- package/modules/import/spec/module.yaml +335 -0
- package/modules/import/src/acl/permissions.ts +6 -0
- package/modules/import/src/api/endpoints.ts +400 -0
- package/modules/import/src/client/ImportJobDrawer.tsrx +229 -0
- package/modules/import/src/client/ImportsView.tsrx +455 -0
- package/modules/import/src/client/NewImportForm.tsrx +274 -0
- package/modules/import/src/client/api.ts +243 -0
- package/modules/import/src/client/contribution.tsrx +29 -0
- package/modules/import/src/client/index.ts +23 -0
- package/modules/import/src/client/navigation.ts +25 -0
- package/modules/import/src/client/presentation.ts +59 -0
- package/modules/import/src/client/state.ts +43 -0
- package/modules/import/src/domain/csv.ts +258 -0
- package/modules/import/src/domain/ports.ts +157 -0
- package/modules/import/src/domain/types.ts +179 -0
- package/modules/import/src/index.ts +58 -0
- package/modules/import/src/platform.ts +60 -0
- package/modules/import/src/server/index.ts +49 -0
- package/modules/import/src/server/runtime.ts +158 -0
- package/modules/import/src/services/csv-source.ts +121 -0
- package/modules/import/src/services/data-classes.ts +63 -0
- package/modules/import/src/services/database-repository.ts +780 -0
- package/modules/import/src/services/import-runner.ts +127 -0
- package/modules/import/src/services/import-service.ts +872 -0
- package/modules/import/src/services/index.ts +6 -0
- package/modules/import/src/services/migration.ts +156 -0
- package/modules/import/src/services/port-registry.ts +158 -0
- package/modules/import/src/services/repository.ts +147 -0
- package/modules/import/src/settings.ts +70 -0
- package/modules/import/translations/en.json +175 -0
- package/modules/import/translations/pl.json +175 -0
- package/modules/metering/migrations/0001_metering_core.down.sql +6 -0
- package/modules/metering/migrations/0001_metering_core.up.sql +119 -0
- package/modules/metering/migrations/0002_metering_bucket_export.down.sql +3 -0
- package/modules/metering/migrations/0002_metering_bucket_export.up.sql +5 -0
- package/modules/metering/migrations/README.md +3 -0
- package/modules/metering/module.json +46 -0
- package/modules/metering/package.json +50 -0
- package/modules/metering/spec/module.yaml +269 -0
- package/modules/metering/src/acl/permissions.ts +5 -0
- package/modules/metering/src/api/endpoints.ts +104 -0
- package/modules/metering/src/cli/commands.json +28 -0
- package/modules/metering/src/cli/index.ts +246 -0
- package/modules/metering/src/client/LimitsView.tsrx +170 -0
- package/modules/metering/src/client/UsageSummaryWidget.tsrx +63 -0
- package/modules/metering/src/client/UsageView.tsrx +205 -0
- package/modules/metering/src/client/api.ts +91 -0
- package/modules/metering/src/client/contribution.tsrx +42 -0
- package/modules/metering/src/client/index.ts +26 -0
- package/modules/metering/src/client/navigation.ts +54 -0
- package/modules/metering/src/client/presentation.ts +63 -0
- package/modules/metering/src/client/state.ts +31 -0
- package/modules/metering/src/domain/meters.ts +100 -0
- package/modules/metering/src/domain/types.ts +71 -0
- package/modules/metering/src/index.ts +51 -0
- package/modules/metering/src/platform.ts +84 -0
- package/modules/metering/src/server/index.ts +40 -0
- package/modules/metering/src/server/runtime.ts +165 -0
- package/modules/metering/src/services/data-classes.ts +34 -0
- package/modules/metering/src/services/database-repository.ts +574 -0
- package/modules/metering/src/services/index.ts +9 -0
- package/modules/metering/src/services/meter-registry.ts +145 -0
- package/modules/metering/src/services/metering-service.ts +441 -0
- package/modules/metering/src/services/migration.ts +183 -0
- package/modules/metering/src/services/notifications.ts +75 -0
- package/modules/metering/src/services/reports.ts +105 -0
- package/modules/metering/src/services/repository.ts +132 -0
- package/modules/metering/src/services/service-error.ts +46 -0
- package/modules/metering/src/settings.ts +47 -0
- package/modules/metering/translations/en.json +68 -0
- package/modules/metering/translations/pl.json +68 -0
- package/modules/notifications/migrations/0001_notifications_core.down.sql +1 -0
- package/modules/notifications/migrations/0001_notifications_core.up.sql +26 -0
- package/modules/notifications/migrations/0002_notifications_preferences.down.sql +1 -0
- package/modules/notifications/migrations/0002_notifications_preferences.up.sql +17 -0
- package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.down.sql +1 -0
- package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.up.sql +30 -0
- package/modules/notifications/migrations/0004_notifications_deliveries.down.sql +1 -0
- package/modules/notifications/migrations/0004_notifications_deliveries.up.sql +35 -0
- package/modules/notifications/migrations/0005_notifications_delivery_routing_role.down.sql +2 -0
- package/modules/notifications/migrations/0005_notifications_delivery_routing_role.up.sql +17 -0
- package/modules/notifications/migrations/0006_notifications_delivery_title.down.sql +1 -0
- package/modules/notifications/migrations/0006_notifications_delivery_title.up.sql +11 -0
- package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.down.sql +2 -0
- package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.up.sql +19 -0
- package/modules/notifications/migrations/0008_notifications_delivery_claim.down.sql +8 -0
- package/modules/notifications/migrations/0008_notifications_delivery_claim.up.sql +19 -0
- package/modules/notifications/migrations/0009_notifications_kind_approvals.down.sql +16 -0
- package/modules/notifications/migrations/0009_notifications_kind_approvals.up.sql +18 -0
- package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.down.sql +16 -0
- package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.up.sql +18 -0
- package/modules/notifications/migrations/0011_notifications_retention_keyset.down.sql +5 -0
- package/modules/notifications/migrations/0011_notifications_retention_keyset.up.sql +15 -0
- package/modules/notifications/migrations/0012_notifications_email_channel.down.sql +12 -0
- package/modules/notifications/migrations/0012_notifications_email_channel.up.sql +38 -0
- package/modules/notifications/migrations/0013_notifications_list_pages.down.sql +5 -0
- package/modules/notifications/migrations/0013_notifications_list_pages.up.sql +13 -0
- package/modules/notifications/migrations/README.md +3 -0
- package/modules/notifications/module.json +32 -0
- package/modules/notifications/package.json +49 -0
- package/modules/notifications/spec/module.yaml +718 -0
- package/modules/notifications/src/acl/permissions.ts +11 -0
- package/modules/notifications/src/api/endpoints.ts +814 -0
- package/modules/notifications/src/cli/commands.json +17 -0
- package/modules/notifications/src/cli/index.ts +126 -0
- package/modules/notifications/src/client/DeliveriesView.tsrx +483 -0
- package/modules/notifications/src/client/FactList.tsrx +24 -0
- package/modules/notifications/src/client/NotificationPreferencesView.tsrx +240 -0
- package/modules/notifications/src/client/NotificationsInboxView.tsrx +592 -0
- package/modules/notifications/src/client/UnreadCountWidget.tsrx +62 -0
- package/modules/notifications/src/client/WebhookForm.tsrx +297 -0
- package/modules/notifications/src/client/WebhooksView.tsrx +637 -0
- package/modules/notifications/src/client/api.ts +382 -0
- package/modules/notifications/src/client/contribution.tsrx +83 -0
- package/modules/notifications/src/client/inbox.ts +41 -0
- package/modules/notifications/src/client/index.ts +29 -0
- package/modules/notifications/src/client/navigation.ts +92 -0
- package/modules/notifications/src/client/notifications.css +70 -0
- package/modules/notifications/src/client/presentation.ts +81 -0
- package/modules/notifications/src/client/state.ts +246 -0
- package/modules/notifications/src/client/unread-count.ts +26 -0
- package/modules/notifications/src/domain/locale.ts +47 -0
- package/modules/notifications/src/domain/publish.ts +46 -0
- package/modules/notifications/src/domain/types.ts +186 -0
- package/modules/notifications/src/index.ts +62 -0
- package/modules/notifications/src/platform.ts +85 -0
- package/modules/notifications/src/server/index.ts +82 -0
- package/modules/notifications/src/server/runtime.ts +251 -0
- package/modules/notifications/src/services/data-classes.ts +139 -0
- package/modules/notifications/src/services/database-repository.ts +1334 -0
- package/modules/notifications/src/services/delivery-payload.ts +93 -0
- package/modules/notifications/src/services/delivery-runner.ts +170 -0
- package/modules/notifications/src/services/delivery-service.ts +811 -0
- package/modules/notifications/src/services/egress.ts +239 -0
- package/modules/notifications/src/services/email-channel.ts +93 -0
- package/modules/notifications/src/services/index.ts +23 -0
- package/modules/notifications/src/services/migration.ts +572 -0
- package/modules/notifications/src/services/notifications-service.ts +297 -0
- package/modules/notifications/src/services/paging.ts +60 -0
- package/modules/notifications/src/services/publish-service.ts +133 -0
- package/modules/notifications/src/services/repository.ts +342 -0
- package/modules/notifications/src/services/secret-rotation.ts +177 -0
- package/modules/notifications/src/services/secret-vault.ts +168 -0
- package/modules/notifications/src/services/service-error.ts +67 -0
- package/modules/notifications/src/services/signature.ts +81 -0
- package/modules/notifications/src/services/webhook-service.ts +307 -0
- package/modules/notifications/src/settings.ts +130 -0
- package/modules/notifications/translations/en.json +255 -0
- package/modules/notifications/translations/pl.json +255 -0
- package/modules/profile/module.json +3 -3
- package/modules/profile/package.json +1 -1
- package/modules/profile/spec/module.yaml +3 -3
- package/modules/profile/src/services/database-repository.ts +3 -13
- package/modules/reports/module.json +28 -0
- package/modules/reports/package.json +42 -0
- package/modules/reports/spec/module.yaml +231 -0
- package/modules/reports/src/acl/permissions.ts +5 -0
- package/modules/reports/src/api/endpoints.ts +72 -0
- package/modules/reports/src/client/ReportsSummaryWidget.tsrx +52 -0
- package/modules/reports/src/client/ReportsView.tsrx +246 -0
- package/modules/reports/src/client/api.ts +65 -0
- package/modules/reports/src/client/contribution.tsrx +36 -0
- package/modules/reports/src/client/index.ts +18 -0
- package/modules/reports/src/client/navigation.ts +39 -0
- package/modules/reports/src/client/presentation.ts +123 -0
- package/modules/reports/src/client/state.ts +28 -0
- package/modules/reports/src/domain/providers.ts +115 -0
- package/modules/reports/src/domain/types.ts +44 -0
- package/modules/reports/src/index.ts +47 -0
- package/modules/reports/src/platform.ts +37 -0
- package/modules/reports/src/server/index.ts +21 -0
- package/modules/reports/src/server/runtime.ts +40 -0
- package/modules/reports/src/services/index.ts +17 -0
- package/modules/reports/src/services/provider-registry.ts +144 -0
- package/modules/reports/src/services/range.ts +65 -0
- package/modules/reports/src/services/reports-service.ts +316 -0
- package/modules/reports/src/services/service-error.ts +26 -0
- package/modules/reports/src/settings.ts +49 -0
- package/modules/reports/translations/en.json +35 -0
- package/modules/reports/translations/pl.json +35 -0
- package/modules/sandbox/module.json +4 -4
- package/modules/sandbox/package.json +1 -1
- package/modules/sandbox/spec/module.yaml +3 -3
- package/modules/sandbox/src/services/database-repository.ts +14 -23
- package/modules/sandbox/src/services/directory.ts +16 -0
- package/modules/sandbox/src/services/sandbox-service.ts +13 -11
- package/modules/search/migrations/0001_search_core.down.sql +1 -0
- package/modules/search/migrations/0001_search_core.up.sql +22 -0
- package/modules/search/migrations/0002_search_recent_export.down.sql +3 -0
- package/modules/search/migrations/0002_search_recent_export.up.sql +5 -0
- package/modules/search/migrations/README.md +3 -0
- package/modules/search/module.json +28 -0
- package/modules/search/package.json +43 -0
- package/modules/search/spec/module.yaml +177 -0
- package/modules/search/src/acl/permissions.ts +5 -0
- package/modules/search/src/api/endpoints.ts +221 -0
- package/modules/search/src/client/SearchView.tsrx +346 -0
- package/modules/search/src/client/api.ts +157 -0
- package/modules/search/src/client/contribution.tsrx +31 -0
- package/modules/search/src/client/index.ts +17 -0
- package/modules/search/src/client/navigation.ts +97 -0
- package/modules/search/src/client/search.css +56 -0
- package/modules/search/src/client/state.ts +113 -0
- package/modules/search/src/domain/data-classes.ts +73 -0
- package/modules/search/src/domain/providers.ts +88 -0
- package/modules/search/src/domain/types.ts +56 -0
- package/modules/search/src/index.ts +46 -0
- package/modules/search/src/platform.ts +42 -0
- package/modules/search/src/server/index.ts +25 -0
- package/modules/search/src/server/runtime.ts +123 -0
- package/modules/search/src/services/database-repository.ts +163 -0
- package/modules/search/src/services/index.ts +21 -0
- package/modules/search/src/services/migration.ts +65 -0
- package/modules/search/src/services/provider-registry.ts +137 -0
- package/modules/search/src/services/repository.ts +43 -0
- package/modules/search/src/services/search-service.ts +442 -0
- package/modules/search/src/services/service-error.ts +35 -0
- package/modules/search/src/settings.ts +68 -0
- package/modules/search/translations/en.json +45 -0
- package/modules/search/translations/pl.json +45 -0
- package/modules/system/module.json +2 -2
- package/modules/system/package.json +1 -1
- package/modules/system/spec/module.yaml +30 -3
- package/modules/system/src/client/FlagsPanel.tsrx +127 -0
- package/modules/system/src/client/ModuleSettingRow.tsrx +17 -7
- package/modules/system/src/client/ModuleSettingsSection.tsrx +10 -71
- package/modules/system/src/client/ModulesView.tsrx +91 -65
- package/modules/system/src/client/flags.ts +35 -0
- package/modules/system/src/client/index.ts +8 -0
- package/modules/system/src/client/navigation.ts +8 -0
- package/modules/system/src/client/settings-state.ts +159 -0
- package/modules/system/src/client/state.ts +2 -0
- package/modules/system/src/domain/time-zone.ts +105 -0
- package/modules/system/src/index.ts +12 -0
- package/modules/system/src/platform.ts +5 -1
- package/modules/system/src/server/endpoints.ts +72 -21
- package/modules/system/src/settings.ts +28 -0
- package/modules/system/translations/en.json +17 -0
- package/modules/system/translations/pl.json +17 -0
- package/modules/users/module.json +30 -4
- package/modules/users/package.json +4 -1
- package/modules/users/spec/module.yaml +290 -7
- package/modules/users/src/api/endpoints.ts +228 -15
- package/modules/users/src/client/InviteMemberForm.tsrx +2 -2
- package/modules/users/src/client/MemberDrawer.tsrx +142 -26
- package/modules/users/src/client/UserAccountForm.tsrx +2 -2
- package/modules/users/src/client/UsersView.tsrx +432 -71
- package/modules/users/src/client/api.ts +106 -12
- package/modules/users/src/client/contribution.tsrx +5 -0
- package/modules/users/src/client/member-columns.tsrx +22 -9
- package/modules/users/src/client/state.ts +172 -7
- package/modules/users/src/domain/lists.ts +9 -0
- package/modules/users/src/index.ts +11 -0
- package/modules/users/src/platform.ts +50 -1
- package/modules/users/src/services/member-export.ts +51 -0
- package/modules/users/src/services/member-import.ts +249 -0
- package/modules/users/src/services/member-search.ts +145 -0
- package/modules/users/src/services/users-service.ts +117 -4
- package/modules/users/translations/en.json +57 -8
- package/modules/users/translations/pl.json +57 -8
- package/modules/workflows/migrations/0004_payload_rotation_inventory.up.sql +17 -0
- package/modules/workflows/migrations/0005_workflows_human_approval.up.sql +28 -0
- package/modules/workflows/migrations/0006_workflows_retention_indexes.up.sql +11 -0
- package/modules/workflows/migrations/0007_workflows_erasure_subject_indexes.up.sql +17 -0
- package/modules/workflows/migrations/0008_workflows_subject_account.up.sql +24 -0
- package/modules/workflows/migrations/0009_workflows_definition_list_indexes.up.sql +9 -0
- package/modules/workflows/module.json +34 -6
- package/modules/workflows/package.json +2 -1
- package/modules/workflows/spec/module.yaml +26 -9
- package/modules/workflows/src/api/endpoints.ts +131 -25
- package/modules/workflows/src/cli/commands.json +17 -0
- package/modules/workflows/src/cli/index.ts +114 -0
- package/modules/workflows/src/client/WorkflowExecutionHistory.tsrx +36 -27
- package/modules/workflows/src/client/WorkflowInspector.tsrx +116 -0
- package/modules/workflows/src/client/WorkflowNodePalette.tsrx +1 -0
- package/modules/workflows/src/client/WorkflowsView.tsrx +236 -99
- package/modules/workflows/src/client/api.ts +54 -34
- package/modules/workflows/src/client/canvas-model.ts +12 -0
- package/modules/workflows/src/client/state.ts +95 -0
- package/modules/workflows/src/domain/events.ts +25 -11
- package/modules/workflows/src/domain/graph.ts +135 -1
- package/modules/workflows/src/domain/types.ts +91 -4
- package/modules/workflows/src/platform.ts +12 -0
- package/modules/workflows/src/server/runtime.ts +78 -5
- package/modules/workflows/src/services/approvals.ts +62 -0
- package/modules/workflows/src/services/cursors.ts +37 -0
- package/modules/workflows/src/services/data-classes.ts +232 -0
- package/modules/workflows/src/services/database-repository.ts +660 -171
- package/modules/workflows/src/services/migration.ts +240 -0
- package/modules/workflows/src/services/notifications.ts +98 -0
- package/modules/workflows/src/services/payload-codec.ts +51 -28
- package/modules/workflows/src/services/payload-rotation.ts +171 -0
- package/modules/workflows/src/services/repository.ts +123 -8
- package/modules/workflows/src/services/simulation.ts +7 -0
- package/modules/workflows/src/services/worker.ts +446 -25
- package/modules/workflows/src/services/workflows-service.ts +264 -103
- package/modules/workflows/translations/en.json +12 -4
- package/modules/workflows/translations/pl.json +12 -4
- package/modules.json +48 -4
- package/package.json +64 -6
- package/packages/client/package.json +1 -0
- package/packages/client/src/ApplicationShell.tsrx +43 -0
- package/packages/client/src/contributions.ts +55 -0
- package/packages/client/src/i18n/locales/en.json +5 -0
- package/packages/client/src/i18n/locales/pl.json +5 -0
- package/packages/client/src/index.ts +13 -1
- package/packages/client/src/routing.ts +22 -0
- package/packages/client/src/shell/CommandPalette.tsrx +223 -11
- package/packages/client/src/shell/command-search.ts +126 -0
- package/packages/client/src/shell/command.ts +30 -0
- package/packages/client/src/shell/navigation.ts +12 -0
- package/packages/client/src/shell/palette-keys.ts +27 -0
- package/packages/client/src/shell/shell.css +7 -0
- package/packages/contracts/schemas/module-spec.schema.json +287 -1
- package/packages/contracts/schemas/module.schema.json +25 -0
- package/packages/contracts/src/index.ts +144 -1
- package/packages/database/src/backup.ts +179 -0
- package/packages/database/src/decoders.ts +25 -0
- package/packages/database/src/index.ts +17 -0
- package/packages/database/src/record-history.ts +5 -14
- package/packages/harness/src/index.ts +2 -0
- package/packages/harness/src/runtime.ts +218 -1
- package/packages/harness/src/tool-adapters.ts +19 -1
- package/packages/kernel/src/acl.ts +179 -0
- package/packages/kernel/src/capability-registry.ts +69 -2
- package/packages/kernel/src/data-class-registry.ts +354 -0
- package/packages/kernel/src/index.ts +65 -4
- package/packages/kernel/src/keyring.ts +208 -0
- package/packages/kernel/src/module-compatibility.ts +35 -4
- package/packages/kernel/src/module-registry.ts +49 -2
- package/packages/kernel/src/module-settings.ts +189 -27
- package/packages/server/package.json +2 -1
- package/packages/server/src/composition.ts +38 -0
- package/packages/server/src/endpoint.ts +104 -47
- package/packages/server/src/export/csv.ts +34 -0
- package/packages/server/src/export/definition.ts +346 -0
- package/packages/server/src/export/index.ts +61 -0
- package/packages/server/src/export/run.ts +116 -0
- package/packages/server/src/index.ts +155 -0
- package/packages/server/src/jobs/index.ts +93 -0
- package/packages/server/src/jobs/runner.ts +486 -0
- package/packages/server/src/log.ts +291 -0
- package/packages/server/src/mail/config.ts +184 -0
- package/packages/server/src/mail/contracts.ts +251 -0
- package/packages/server/src/mail/index.ts +31 -0
- package/packages/server/src/mail/port.ts +101 -0
- package/packages/server/src/mail/smtp.ts +191 -0
- package/packages/server/src/mail/template.ts +105 -0
- package/packages/server/src/metrics.ts +447 -0
- package/packages/server/src/pagination.ts +246 -0
- package/packages/server/src/trace/context.ts +103 -0
- package/packages/server/src/trace/egress.ts +66 -0
- package/packages/server/src/trace/error-sink.ts +296 -0
- package/packages/server/src/trace/exporter.ts +295 -0
- package/packages/server/src/trace/index.ts +60 -0
- package/packages/server/src/trace/job-sink.ts +172 -0
- package/packages/server/src/trace/tracer.ts +338 -0
- package/packages/server/src/web.ts +5 -1
- package/packages/storage/package.json +40 -0
- package/packages/storage/src/config.ts +189 -0
- package/packages/storage/src/content-type.ts +124 -0
- package/packages/storage/src/contracts.ts +112 -0
- package/packages/storage/src/envelope.ts +189 -0
- package/packages/storage/src/index.ts +43 -0
- package/packages/storage/src/local.ts +75 -0
- package/packages/storage/src/port.ts +251 -0
- package/packages/storage/src/read-token.ts +130 -0
- package/packages/storage/src/s3.ts +144 -0
- package/packages/storage/src/scanner.ts +22 -0
- package/packages/storage/src/sigv4.ts +104 -0
- package/packages/storage/src/store.ts +13 -0
- package/packages/ui/package.json +1 -0
- package/packages/ui/src/components/Button.tsrx +3 -0
- package/packages/ui/src/components/ConfirmDialog.tsrx +13 -2
- package/packages/ui/src/components/DateField.tsrx +92 -0
- package/packages/ui/src/components/DatePicker.tsrx +525 -0
- package/packages/ui/src/components/DateRangeField.tsrx +93 -0
- package/packages/ui/src/components/Drawer.tsrx +10 -1
- package/packages/ui/src/components/FileUpload.tsrx +158 -0
- package/packages/ui/src/components/Pagination.tsrx +130 -0
- package/packages/ui/src/components/Select.tsrx +78 -0
- package/packages/ui/src/components/Table.tsrx +498 -81
- package/packages/ui/src/components/Tabs.tsrx +53 -0
- package/packages/ui/src/components/ToastHost.tsrx +48 -0
- package/packages/ui/src/components/calendar.ts +243 -0
- package/packages/ui/src/components/date-field.ts +62 -0
- package/packages/ui/src/components/file-upload.ts +49 -0
- package/packages/ui/src/components/focus-trap.ts +62 -0
- package/packages/ui/src/components/pagination.ts +68 -0
- package/packages/ui/src/components/table-sorting.ts +49 -0
- package/packages/ui/src/components/tabs.ts +61 -0
- package/packages/ui/src/components/toast-store.ts +116 -0
- package/packages/ui/src/icons/Icon.tsrx +5 -0
- package/packages/ui/src/index.ts +45 -0
- package/packages/ui/src/styles/components.css +598 -0
- package/modules/automations-workflows-integration/module.json +0 -27
- package/modules/automations-workflows-integration/spec/module.yaml +0 -93
- package/modules/automations-workflows-integration/src/index.ts +0 -14
- package/modules/automations-workflows-integration/src/platform.ts +0 -24
- package/modules/automations-workflows-integration/src/server/index.ts +0 -11
- package/modules/automations-workflows-integration/translations/en.json +0 -21
- package/modules/automations-workflows-integration/translations/pl.json +0 -21
- package/modules/workflows/src/services/cursor-codec.ts +0 -62
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
import { mkdir, open } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type {
|
|
4
|
+
DataClassCountInput,
|
|
5
|
+
DataClassErasureInput,
|
|
6
|
+
DataClassErasureResult,
|
|
7
|
+
PlatformDataClassRegistry,
|
|
8
|
+
} from '@flowdular/sdk/kernel';
|
|
9
|
+
import { JOB_CLAIM_LOST } from '@flowdular/sdk/server';
|
|
10
|
+
import {
|
|
11
|
+
AUDIT_EVENT_ACTIONS,
|
|
12
|
+
AUDIT_REASONS,
|
|
13
|
+
type AuditErasureRun,
|
|
14
|
+
type AuditSubjectKey,
|
|
15
|
+
type ErasureClassOutcome,
|
|
16
|
+
} from '../domain/types.ts';
|
|
17
|
+
import { DeclaredDataClasses } from './declared-classes.ts';
|
|
18
|
+
import { exportOutputDirectory } from './export-directory.ts';
|
|
19
|
+
import { AuditHoldService } from './hold-service.ts';
|
|
20
|
+
import { ERASURE_LIMITS, type AuditErasureRegistry } from './erasure-port.ts';
|
|
21
|
+
import type { AuditRepository } from './repository.ts';
|
|
22
|
+
import { AuditServiceError, bounded } from './service-error.ts';
|
|
23
|
+
import { erasureSubjectMarker } from './subject-keys.ts';
|
|
24
|
+
|
|
25
|
+
export const ERASURE_CERTIFICATE_VERSION = 'audit-erasure/1';
|
|
26
|
+
|
|
27
|
+
/** Requested runs one pass takes, so one pass never holds the loop. */
|
|
28
|
+
export const ERASURE_ROUTING_PAGE = 10;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How often the platform looks for requested erasures. An operator waits for
|
|
32
|
+
* the answer, so the loop is as responsive as the export one; an idle
|
|
33
|
+
* deployment pays one indexed routing read per interval.
|
|
34
|
+
*/
|
|
35
|
+
export const ERASURE_POLL_INTERVAL_MS = 5_000;
|
|
36
|
+
|
|
37
|
+
/** How long a claim holds a run whose process died mid-erasure. */
|
|
38
|
+
export const ERASURE_CLAIM_TIMEOUT_MS = 15 * 60_000;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* How long a request nobody answered stays requested. A command records the row
|
|
42
|
+
* and waits ten minutes; a deployment whose platform was down for longer must
|
|
43
|
+
* not carry the request for ever, or the ledger grows with work that will never
|
|
44
|
+
* be performed and the routing read pages through it on every interval.
|
|
45
|
+
*/
|
|
46
|
+
export const ERASURE_REQUEST_TTL_MS = 24 * 60 * 60_000;
|
|
47
|
+
|
|
48
|
+
/** How long the operator command waits for the platform to answer a run. */
|
|
49
|
+
export const ERASURE_WAIT = {
|
|
50
|
+
timeoutMs: 10 * 60_000,
|
|
51
|
+
pollMs: 1_000,
|
|
52
|
+
} as const;
|
|
53
|
+
|
|
54
|
+
export type { ErasureClassOutcome };
|
|
55
|
+
export { erasureSubjectMarker };
|
|
56
|
+
|
|
57
|
+
export interface ErasureCertificate {
|
|
58
|
+
readonly formatVersion: string;
|
|
59
|
+
readonly workspace: {
|
|
60
|
+
readonly tenantId: string;
|
|
61
|
+
readonly slug: string | null;
|
|
62
|
+
readonly name: string | null;
|
|
63
|
+
};
|
|
64
|
+
readonly subject: string;
|
|
65
|
+
readonly applied: boolean;
|
|
66
|
+
/** False when a class was truncated or failed, so the subject is still in one. */
|
|
67
|
+
readonly complete: boolean;
|
|
68
|
+
readonly classes: readonly ErasureClassOutcome[];
|
|
69
|
+
readonly totals: {
|
|
70
|
+
readonly classes: number;
|
|
71
|
+
/** Rows the owners took out; a redacted row is not one of them. */
|
|
72
|
+
readonly rows: number;
|
|
73
|
+
/** Rows they kept but stripped of the subject, in a ledger or a snapshot. */
|
|
74
|
+
readonly redacted: number;
|
|
75
|
+
/** Classes that declare no erase operation, named on the certificate. */
|
|
76
|
+
readonly notErasable: number;
|
|
77
|
+
};
|
|
78
|
+
readonly subjectKey: {
|
|
79
|
+
readonly requested: boolean;
|
|
80
|
+
readonly state: string | null;
|
|
81
|
+
readonly destroyedAt: string | null;
|
|
82
|
+
};
|
|
83
|
+
/** The newest anchor of the workspace, so the certificate names the chain. */
|
|
84
|
+
readonly anchor: {
|
|
85
|
+
readonly anchorSequence: number;
|
|
86
|
+
readonly toSequence: number;
|
|
87
|
+
readonly anchorHash: string;
|
|
88
|
+
} | null;
|
|
89
|
+
readonly operator: string;
|
|
90
|
+
readonly startedAt: string;
|
|
91
|
+
readonly completedAt: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ErasureResult {
|
|
95
|
+
readonly applied: boolean;
|
|
96
|
+
readonly subject: string;
|
|
97
|
+
readonly classes: readonly ErasureClassOutcome[];
|
|
98
|
+
/** False when a class was truncated or failed; the run is recorded partial. */
|
|
99
|
+
readonly complete: boolean;
|
|
100
|
+
readonly certificate: ErasureCertificate | null;
|
|
101
|
+
readonly certificatePath: string | null;
|
|
102
|
+
readonly subjectKey: AuditSubjectKey | null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ErasureRequest {
|
|
106
|
+
readonly tenantId: string;
|
|
107
|
+
readonly subject: string;
|
|
108
|
+
readonly slug?: string | null;
|
|
109
|
+
readonly name?: string | null;
|
|
110
|
+
readonly operator: string;
|
|
111
|
+
/** Absolute and inside the directory the deployment allows. */
|
|
112
|
+
readonly outputDirectory: string;
|
|
113
|
+
readonly apply: boolean;
|
|
114
|
+
/** Also destroys the subject's audit data key, for ever. */
|
|
115
|
+
readonly destroyKey: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ErasureServiceOptions {
|
|
119
|
+
readonly repository: AuditRepository;
|
|
120
|
+
readonly holds: AuditHoldService;
|
|
121
|
+
/**
|
|
122
|
+
* The sealed platform catalogue. Every class it carries gets an outcome, so
|
|
123
|
+
* a plan and a certificate name the whole deployment rather than only the
|
|
124
|
+
* classes that happen to be erasable.
|
|
125
|
+
*/
|
|
126
|
+
readonly dataClasses: PlatformDataClassRegistry;
|
|
127
|
+
/** The audit.erasure.v1 adapter, for a class that registered there instead. */
|
|
128
|
+
readonly adapter?: AuditErasureRegistry;
|
|
129
|
+
readonly environment: NodeJS.ProcessEnv;
|
|
130
|
+
readonly workspaceRoot: string;
|
|
131
|
+
readonly now?: () => number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** One class as the run sees it: the declaration and the adapter resolved once. */
|
|
135
|
+
interface ErasableClass {
|
|
136
|
+
readonly moduleId: string;
|
|
137
|
+
readonly classId: string;
|
|
138
|
+
readonly erase:
|
|
139
|
+
| ((input: DataClassErasureInput) => Promise<DataClassErasureResult>)
|
|
140
|
+
| null;
|
|
141
|
+
readonly count:
|
|
142
|
+
| ((input: DataClassCountInput) => Promise<number | null>)
|
|
143
|
+
| null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Erasure on request. audit.core owns no foreign table, so a class is erased by
|
|
148
|
+
* the module that owns it, through the operation its data class declaration
|
|
149
|
+
* carries; audit.core orders the run, refuses it under a hold, writes the trail
|
|
150
|
+
* around it and hands the operator a certificate that names every class of the
|
|
151
|
+
* deployment and what happened to it.
|
|
152
|
+
*/
|
|
153
|
+
export class AuditErasureService {
|
|
154
|
+
readonly #repository: AuditRepository;
|
|
155
|
+
readonly #holds: AuditHoldService;
|
|
156
|
+
readonly #declared: DeclaredDataClasses;
|
|
157
|
+
readonly #adapter: AuditErasureRegistry | null;
|
|
158
|
+
readonly #environment: NodeJS.ProcessEnv;
|
|
159
|
+
readonly #workspaceRoot: string;
|
|
160
|
+
readonly #now: () => number;
|
|
161
|
+
|
|
162
|
+
constructor(options: ErasureServiceOptions) {
|
|
163
|
+
this.#repository = options.repository;
|
|
164
|
+
this.#holds = options.holds;
|
|
165
|
+
this.#declared = new DeclaredDataClasses(options.dataClasses);
|
|
166
|
+
this.#adapter = options.adapter ?? null;
|
|
167
|
+
this.#environment = options.environment;
|
|
168
|
+
this.#workspaceRoot = options.workspaceRoot;
|
|
169
|
+
this.#now = options.now ?? Date.now;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Records what the operator asked for. The directory is checked here so the
|
|
174
|
+
* command answers a bad request immediately instead of recording a run the
|
|
175
|
+
* platform will only refuse.
|
|
176
|
+
*/
|
|
177
|
+
async request(
|
|
178
|
+
input: ErasureRequest & { readonly subjectMarker?: string },
|
|
179
|
+
): Promise<AuditErasureRun> {
|
|
180
|
+
const subject = bounded(input.subject, 'subject', 1, 64);
|
|
181
|
+
return this.#repository.startErasureRun({
|
|
182
|
+
tenantId: input.tenantId,
|
|
183
|
+
subject,
|
|
184
|
+
subjectMarker:
|
|
185
|
+
input.subjectMarker ?? erasureSubjectMarker(input.tenantId, subject),
|
|
186
|
+
requestedBy: input.operator,
|
|
187
|
+
/* Resolved for a plan too: an operator learns that the directory is
|
|
188
|
+
refused before being told how much would be removed. */
|
|
189
|
+
outputDirectory: this.#directory(input.outputDirectory),
|
|
190
|
+
dryRun: !input.apply,
|
|
191
|
+
destroyKey: input.destroyKey,
|
|
192
|
+
workspaceSlug: input.slug ?? null,
|
|
193
|
+
workspaceName: input.name ?? null,
|
|
194
|
+
startedAt: this.#now(),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Answers one claimed run. It records a refusal as the run's own outcome
|
|
200
|
+
* rather than throwing, because the operator reads the ledger, not this
|
|
201
|
+
* process's log. The one thing it does throw is a lost claim: that run
|
|
202
|
+
* belongs to the process holding it now, and this one settles nothing.
|
|
203
|
+
*/
|
|
204
|
+
async perform(
|
|
205
|
+
record: AuditErasureRun,
|
|
206
|
+
signal?: AbortSignal,
|
|
207
|
+
): Promise<AuditErasureRun> {
|
|
208
|
+
/* The caller claimed the row first, so the request this process expires is
|
|
209
|
+
one no other process is performing. */
|
|
210
|
+
if (record.startedAt <= this.#now() - ERASURE_REQUEST_TTL_MS) {
|
|
211
|
+
return (
|
|
212
|
+
(await this.#finish(record, 'failed', {
|
|
213
|
+
reason: AUDIT_REASONS.erasureRequestExpired,
|
|
214
|
+
})) ?? record
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const subject = record.subject;
|
|
218
|
+
if (!subject) {
|
|
219
|
+
return (
|
|
220
|
+
(await this.#finish(record, 'failed', {
|
|
221
|
+
reason: 'ERASURE_SUBJECT_MISSING',
|
|
222
|
+
})) ?? record
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
const result = await this.run(
|
|
227
|
+
{
|
|
228
|
+
tenantId: record.tenantId,
|
|
229
|
+
subject,
|
|
230
|
+
slug: record.workspaceSlug,
|
|
231
|
+
name: record.workspaceName,
|
|
232
|
+
operator: record.requestedBy,
|
|
233
|
+
outputDirectory: record.outputDirectory ?? '',
|
|
234
|
+
apply: !record.dryRun,
|
|
235
|
+
destroyKey: record.destroyKey,
|
|
236
|
+
},
|
|
237
|
+
signal,
|
|
238
|
+
);
|
|
239
|
+
return (
|
|
240
|
+
(await this.#finish(
|
|
241
|
+
record,
|
|
242
|
+
result.applied && !result.complete ? 'partial' : 'completed',
|
|
243
|
+
{
|
|
244
|
+
classes: result.classes.length,
|
|
245
|
+
rows: result.classes.reduce(
|
|
246
|
+
(total, entry) => total + (entry.rows ?? 0),
|
|
247
|
+
0,
|
|
248
|
+
),
|
|
249
|
+
certificatePath: result.certificatePath,
|
|
250
|
+
outcome: result.classes,
|
|
251
|
+
},
|
|
252
|
+
)) ?? record
|
|
253
|
+
);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
/* The lease lapsed and another loop reclaimed the run while this one
|
|
256
|
+
was working. Recording a refusal here would settle a row that is
|
|
257
|
+
somebody else's work now. */
|
|
258
|
+
if (signal?.aborted) throw error;
|
|
259
|
+
return (
|
|
260
|
+
(await this.#finish(record, 'failed', {
|
|
261
|
+
reason:
|
|
262
|
+
error instanceof AuditServiceError
|
|
263
|
+
? error.code.slice(0, 64)
|
|
264
|
+
: 'ERASURE_FAILED',
|
|
265
|
+
})) ?? record
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The claim fence, read once per class. The runner renews the claim on its
|
|
272
|
+
* own timer and aborts this signal when a renewal matches nothing, so a run
|
|
273
|
+
* longer than the lease stops between two classes instead of erasing on
|
|
274
|
+
* behalf of a claim it no longer holds.
|
|
275
|
+
*/
|
|
276
|
+
#checkpoint(signal: AbortSignal | undefined): void {
|
|
277
|
+
if (!signal?.aborted) return;
|
|
278
|
+
throw new AuditServiceError(
|
|
279
|
+
JOB_CLAIM_LOST,
|
|
280
|
+
'Another process took this erasure run over while it was running.',
|
|
281
|
+
409,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async #finish(
|
|
286
|
+
record: AuditErasureRun,
|
|
287
|
+
status: 'completed' | 'partial' | 'failed',
|
|
288
|
+
fields: {
|
|
289
|
+
readonly classes?: number;
|
|
290
|
+
readonly rows?: number;
|
|
291
|
+
readonly certificatePath?: string | null;
|
|
292
|
+
readonly outcome?: readonly ErasureClassOutcome[] | null;
|
|
293
|
+
readonly reason?: string | null;
|
|
294
|
+
},
|
|
295
|
+
): Promise<AuditErasureRun | null> {
|
|
296
|
+
return this.#repository.finishErasureRun({
|
|
297
|
+
tenantId: record.tenantId,
|
|
298
|
+
id: record.id,
|
|
299
|
+
status,
|
|
300
|
+
classes: fields.classes ?? 0,
|
|
301
|
+
rows: fields.rows ?? 0,
|
|
302
|
+
certificatePath: fields.certificatePath ?? null,
|
|
303
|
+
outcome: fields.outcome ?? null,
|
|
304
|
+
reason: fields.reason ?? null,
|
|
305
|
+
completedAt: this.#now(),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async run(
|
|
310
|
+
request: ErasureRequest,
|
|
311
|
+
signal?: AbortSignal,
|
|
312
|
+
): Promise<ErasureResult> {
|
|
313
|
+
const subject = bounded(request.subject, 'subject', 1, 64);
|
|
314
|
+
const startedAt = this.#now();
|
|
315
|
+
/* Checked before anything is counted: a plan that walked every owner
|
|
316
|
+
under a hold would already have read what it may not touch. */
|
|
317
|
+
const hold = await this.#holds.forSubject(request.tenantId, subject);
|
|
318
|
+
if (hold.held) {
|
|
319
|
+
throw new AuditServiceError(
|
|
320
|
+
AUDIT_REASONS.holdActive,
|
|
321
|
+
`A legal hold covers ${subject} in this workspace (${hold.holdIds.join(', ')}). Lift it before erasing.`,
|
|
322
|
+
409,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
/* The directory is resolved before any row is removed, so an erasure
|
|
326
|
+
never happens without somewhere to write the certificate that proves
|
|
327
|
+
it did. */
|
|
328
|
+
const directory = request.apply
|
|
329
|
+
? this.#directory(request.outputDirectory)
|
|
330
|
+
: null;
|
|
331
|
+
const classes = this.#classes();
|
|
332
|
+
const marker = erasureSubjectMarker(request.tenantId, subject);
|
|
333
|
+
if (!request.apply) {
|
|
334
|
+
const planned = await this.#plan(
|
|
335
|
+
request.tenantId,
|
|
336
|
+
subject,
|
|
337
|
+
classes,
|
|
338
|
+
signal,
|
|
339
|
+
);
|
|
340
|
+
return {
|
|
341
|
+
applied: false,
|
|
342
|
+
subject,
|
|
343
|
+
classes: planned,
|
|
344
|
+
complete: planned.every((entry) => entry.outcome !== 'failed'),
|
|
345
|
+
certificate: null,
|
|
346
|
+
certificatePath: null,
|
|
347
|
+
subjectKey: await this.#subjectKey(request.tenantId, subject, marker),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/* A subject whose key was destroyed by an earlier run must not be sealed
|
|
351
|
+
under a new one: that would put the account back in the clear in the
|
|
352
|
+
very events the destruction made unreadable. */
|
|
353
|
+
const keyBefore = await this.#subjectKey(request.tenantId, subject, marker);
|
|
354
|
+
const keyGone = keyBefore?.state === 'destroyed';
|
|
355
|
+
await this.#event(
|
|
356
|
+
request,
|
|
357
|
+
subject,
|
|
358
|
+
AUDIT_EVENT_ACTIONS.erasureStarted,
|
|
359
|
+
startedAt,
|
|
360
|
+
{ classes: classes.length, destroyKey: request.destroyKey },
|
|
361
|
+
keyGone,
|
|
362
|
+
);
|
|
363
|
+
const outcomes: ErasureClassOutcome[] = [];
|
|
364
|
+
for (const entry of classes) {
|
|
365
|
+
this.#checkpoint(signal);
|
|
366
|
+
outcomes.push(await this.#erase(entry, request.tenantId, subject));
|
|
367
|
+
}
|
|
368
|
+
/* Asked once more before the irreversible half: destroying a subject key
|
|
369
|
+
and signing a certificate for a claim this process no longer holds
|
|
370
|
+
cannot be taken back, and the run belongs to its new owner. */
|
|
371
|
+
this.#checkpoint(signal);
|
|
372
|
+
/* The key goes before the closing event is written: an event sealed under
|
|
373
|
+
a key that was just destroyed would create a new one and leave the
|
|
374
|
+
subject readable again. */
|
|
375
|
+
const subjectKey = request.destroyKey
|
|
376
|
+
? await this.#destroyKey(request, subject, marker)
|
|
377
|
+
: keyBefore;
|
|
378
|
+
const destroyed = subjectKey?.state === 'destroyed';
|
|
379
|
+
const completedAt = this.#now();
|
|
380
|
+
const certificate = this.#certificate({
|
|
381
|
+
request,
|
|
382
|
+
subject,
|
|
383
|
+
classes: outcomes,
|
|
384
|
+
subjectKey,
|
|
385
|
+
anchor: await this.#repository.latestAnchor(request.tenantId),
|
|
386
|
+
startedAt,
|
|
387
|
+
completedAt,
|
|
388
|
+
});
|
|
389
|
+
const certificatePath = await this.#writeCertificate(
|
|
390
|
+
directory!,
|
|
391
|
+
request.tenantId,
|
|
392
|
+
certificate,
|
|
393
|
+
);
|
|
394
|
+
await this.#event(
|
|
395
|
+
request,
|
|
396
|
+
subject,
|
|
397
|
+
AUDIT_EVENT_ACTIONS.erasureCompleted,
|
|
398
|
+
completedAt,
|
|
399
|
+
{
|
|
400
|
+
classes: certificate.totals.classes,
|
|
401
|
+
rows: certificate.totals.rows,
|
|
402
|
+
complete: certificate.complete,
|
|
403
|
+
certificate: certificatePath,
|
|
404
|
+
keyDestroyed: destroyed,
|
|
405
|
+
},
|
|
406
|
+
destroyed || keyGone,
|
|
407
|
+
);
|
|
408
|
+
return {
|
|
409
|
+
applied: true,
|
|
410
|
+
subject,
|
|
411
|
+
classes: outcomes,
|
|
412
|
+
complete: certificate.complete,
|
|
413
|
+
certificate,
|
|
414
|
+
certificatePath,
|
|
415
|
+
subjectKey,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Every class of the sealed catalogue, with its erase and count resolved
|
|
421
|
+
* once. The declaration is the source of truth and the audit.erasure.v1
|
|
422
|
+
* adapter only answers for a class whose declaration carries neither, so a
|
|
423
|
+
* module is never asked twice for one class.
|
|
424
|
+
*/
|
|
425
|
+
#classes(): readonly ErasableClass[] {
|
|
426
|
+
return this.#declared.all().map((declared) => {
|
|
427
|
+
const registered = this.#adapter?.get(declared.classId) ?? null;
|
|
428
|
+
return {
|
|
429
|
+
moduleId: declared.moduleId,
|
|
430
|
+
classId: declared.classId,
|
|
431
|
+
erase: declared.declaration.erase ?? registered?.erase ?? null,
|
|
432
|
+
count: declared.declaration.count ?? registered?.count ?? null,
|
|
433
|
+
};
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** The subject's key, or the tombstone an earlier destruction left behind. */
|
|
438
|
+
async #subjectKey(
|
|
439
|
+
tenantId: string,
|
|
440
|
+
subject: string,
|
|
441
|
+
marker: string,
|
|
442
|
+
): Promise<AuditSubjectKey | null> {
|
|
443
|
+
const active = await this.#repository.getSubjectKey(tenantId, subject);
|
|
444
|
+
if (active) return active;
|
|
445
|
+
return this.#repository.getSubjectKeyByMarker(tenantId, marker);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async #plan(
|
|
449
|
+
tenantId: string,
|
|
450
|
+
subject: string,
|
|
451
|
+
classes: readonly ErasableClass[],
|
|
452
|
+
signal: AbortSignal | undefined,
|
|
453
|
+
): Promise<readonly ErasureClassOutcome[]> {
|
|
454
|
+
const outcomes: ErasureClassOutcome[] = [];
|
|
455
|
+
for (const entry of classes) {
|
|
456
|
+
this.#checkpoint(signal);
|
|
457
|
+
const outcome = entry.erase ? 'erasable' : 'not-erasable';
|
|
458
|
+
if (!entry.count) {
|
|
459
|
+
outcomes.push({
|
|
460
|
+
moduleId: entry.moduleId,
|
|
461
|
+
classId: entry.classId,
|
|
462
|
+
outcome,
|
|
463
|
+
rows: null,
|
|
464
|
+
});
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
/* Foreign code. A class that cannot answer must not stop the plan:
|
|
469
|
+
the operator still needs to see the rest before deciding. */
|
|
470
|
+
const rows = await entry.count({
|
|
471
|
+
tenantId,
|
|
472
|
+
subject: { accountId: subject },
|
|
473
|
+
});
|
|
474
|
+
outcomes.push({
|
|
475
|
+
moduleId: entry.moduleId,
|
|
476
|
+
classId: entry.classId,
|
|
477
|
+
outcome,
|
|
478
|
+
rows: rows === null ? null : Math.max(0, Math.trunc(rows)),
|
|
479
|
+
});
|
|
480
|
+
} catch (error) {
|
|
481
|
+
outcomes.push({
|
|
482
|
+
moduleId: entry.moduleId,
|
|
483
|
+
classId: entry.classId,
|
|
484
|
+
outcome,
|
|
485
|
+
rows: null,
|
|
486
|
+
failure: message(error),
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return outcomes;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async #erase(
|
|
494
|
+
entry: ErasableClass,
|
|
495
|
+
tenantId: string,
|
|
496
|
+
subject: string,
|
|
497
|
+
): Promise<ErasureClassOutcome> {
|
|
498
|
+
if (!entry.erase) {
|
|
499
|
+
return {
|
|
500
|
+
moduleId: entry.moduleId,
|
|
501
|
+
classId: entry.classId,
|
|
502
|
+
outcome: 'not-erasable',
|
|
503
|
+
rows: null,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
let erased = 0;
|
|
507
|
+
let redacted = 0;
|
|
508
|
+
let truncated = false;
|
|
509
|
+
for (let batch = 1; batch <= ERASURE_LIMITS.batches; batch += 1) {
|
|
510
|
+
let answer: DataClassErasureResult;
|
|
511
|
+
try {
|
|
512
|
+
/* Foreign code. A failing class is recorded on the certificate and
|
|
513
|
+
the run continues, because a subject erased from four of five
|
|
514
|
+
classes is better than one erased from none, and the certificate
|
|
515
|
+
is what says which. */
|
|
516
|
+
answer = await entry.erase({
|
|
517
|
+
tenantId,
|
|
518
|
+
subject: { accountId: subject },
|
|
519
|
+
limit: ERASURE_LIMITS.batch,
|
|
520
|
+
});
|
|
521
|
+
} catch (error) {
|
|
522
|
+
return {
|
|
523
|
+
moduleId: entry.moduleId,
|
|
524
|
+
classId: entry.classId,
|
|
525
|
+
outcome: 'failed',
|
|
526
|
+
rows: erased,
|
|
527
|
+
...(redacted > 0 ? { redacted } : {}),
|
|
528
|
+
failure: message(error),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
/* An unreadable count is no progress: the class is recorded truncated
|
|
532
|
+
instead of looping through every batch on a NaN comparison. */
|
|
533
|
+
const removed = progressCount(answer?.removed);
|
|
534
|
+
const stripped = progressCount(answer?.redacted);
|
|
535
|
+
if (removed === null || stripped === null) {
|
|
536
|
+
truncated = true;
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
erased += removed;
|
|
540
|
+
redacted += stripped;
|
|
541
|
+
truncated = answer?.truncated === true;
|
|
542
|
+
/* A row the owner kept but stripped of the subject is as much progress
|
|
543
|
+
as one it took, so a class that can only redact runs to its end. A
|
|
544
|
+
class that did neither has nothing left to do, whatever it says about
|
|
545
|
+
the rest; its own flag is still what the certificate records. */
|
|
546
|
+
const cleared = removed + stripped;
|
|
547
|
+
if (cleared === 0) break;
|
|
548
|
+
if (!truncated && cleared < ERASURE_LIMITS.batch) break;
|
|
549
|
+
if (batch === ERASURE_LIMITS.batches) truncated = true;
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
moduleId: entry.moduleId,
|
|
553
|
+
classId: entry.classId,
|
|
554
|
+
outcome: 'erased',
|
|
555
|
+
rows: erased,
|
|
556
|
+
...(redacted > 0 ? { redacted } : {}),
|
|
557
|
+
...(truncated ? { truncated: true } : {}),
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async #destroyKey(
|
|
562
|
+
request: ErasureRequest,
|
|
563
|
+
subject: string,
|
|
564
|
+
marker: string,
|
|
565
|
+
): Promise<AuditSubjectKey | null> {
|
|
566
|
+
const destroyed = await this.#repository.destroySubjectKey(
|
|
567
|
+
request.tenantId,
|
|
568
|
+
subject,
|
|
569
|
+
this.#now(),
|
|
570
|
+
);
|
|
571
|
+
if (!destroyed) {
|
|
572
|
+
/* Already a tombstone: found by its marker, because the account it
|
|
573
|
+
belonged to is exactly what destruction removed. */
|
|
574
|
+
return this.#repository.getSubjectKeyByMarker(request.tenantId, marker);
|
|
575
|
+
}
|
|
576
|
+
/* Written after the key is gone and never sealed under it: this event is
|
|
577
|
+
the proof the destruction happened and must stay readable. */
|
|
578
|
+
await this.#repository.appendAuditEvent({
|
|
579
|
+
tenantId: request.tenantId,
|
|
580
|
+
actorId: request.operator,
|
|
581
|
+
action: AUDIT_EVENT_ACTIONS.subjectKeyDestroyed,
|
|
582
|
+
subjectType: 'erasure',
|
|
583
|
+
subjectId: destroyed.id,
|
|
584
|
+
metadata: { subjectKeyId: destroyed.id },
|
|
585
|
+
occurredAt: this.#now(),
|
|
586
|
+
});
|
|
587
|
+
return destroyed;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
#certificate(input: {
|
|
591
|
+
readonly request: ErasureRequest;
|
|
592
|
+
readonly subject: string;
|
|
593
|
+
readonly classes: readonly ErasureClassOutcome[];
|
|
594
|
+
readonly subjectKey: AuditSubjectKey | null;
|
|
595
|
+
readonly anchor: Awaited<ReturnType<AuditRepository['latestAnchor']>>;
|
|
596
|
+
readonly startedAt: number;
|
|
597
|
+
readonly completedAt: number;
|
|
598
|
+
}): ErasureCertificate {
|
|
599
|
+
return {
|
|
600
|
+
formatVersion: ERASURE_CERTIFICATE_VERSION,
|
|
601
|
+
workspace: {
|
|
602
|
+
tenantId: input.request.tenantId,
|
|
603
|
+
slug: input.request.slug ?? null,
|
|
604
|
+
name: input.request.name ?? null,
|
|
605
|
+
},
|
|
606
|
+
subject: input.subject,
|
|
607
|
+
applied: true,
|
|
608
|
+
complete: input.classes.every(
|
|
609
|
+
(entry) => entry.outcome !== 'failed' && entry.truncated !== true,
|
|
610
|
+
),
|
|
611
|
+
classes: input.classes,
|
|
612
|
+
totals: {
|
|
613
|
+
classes: input.classes.length,
|
|
614
|
+
rows: input.classes.reduce(
|
|
615
|
+
(total, entry) => total + (entry.rows ?? 0),
|
|
616
|
+
0,
|
|
617
|
+
),
|
|
618
|
+
redacted: input.classes.reduce(
|
|
619
|
+
(total, entry) => total + (entry.redacted ?? 0),
|
|
620
|
+
0,
|
|
621
|
+
),
|
|
622
|
+
notErasable: input.classes.filter(
|
|
623
|
+
(entry) => entry.outcome === 'not-erasable',
|
|
624
|
+
).length,
|
|
625
|
+
},
|
|
626
|
+
subjectKey: {
|
|
627
|
+
requested: input.request.destroyKey,
|
|
628
|
+
state: input.subjectKey?.state ?? null,
|
|
629
|
+
destroyedAt:
|
|
630
|
+
input.subjectKey?.destroyedAt == null
|
|
631
|
+
? null
|
|
632
|
+
: new Date(input.subjectKey.destroyedAt).toISOString(),
|
|
633
|
+
},
|
|
634
|
+
anchor: input.anchor
|
|
635
|
+
? {
|
|
636
|
+
anchorSequence: input.anchor.anchorSequence,
|
|
637
|
+
toSequence: input.anchor.toSequence,
|
|
638
|
+
anchorHash: input.anchor.anchorHash,
|
|
639
|
+
}
|
|
640
|
+
: null,
|
|
641
|
+
operator: input.request.operator,
|
|
642
|
+
startedAt: new Date(input.startedAt).toISOString(),
|
|
643
|
+
completedAt: new Date(input.completedAt).toISOString(),
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async #writeCertificate(
|
|
648
|
+
directory: string,
|
|
649
|
+
tenantId: string,
|
|
650
|
+
certificate: ErasureCertificate,
|
|
651
|
+
): Promise<string> {
|
|
652
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
653
|
+
const stamp = new Date(certificate.completedAt)
|
|
654
|
+
.toISOString()
|
|
655
|
+
.replaceAll(/[:.]/g, '')
|
|
656
|
+
.replaceAll('-', '');
|
|
657
|
+
/* The subject is hashed into the name: a directory listing must not be a
|
|
658
|
+
list of the people a workspace erased. */
|
|
659
|
+
const marker = erasureSubjectMarker(tenantId, certificate.subject);
|
|
660
|
+
const path = resolve(
|
|
661
|
+
directory,
|
|
662
|
+
`audit-erasure-${safe(tenantId)}-${stamp}-${marker}.json`,
|
|
663
|
+
);
|
|
664
|
+
const handle = await open(path, 'wx', 0o600);
|
|
665
|
+
try {
|
|
666
|
+
await handle.write(`${JSON.stringify(certificate, null, '\t')}\n`);
|
|
667
|
+
} finally {
|
|
668
|
+
await handle.close();
|
|
669
|
+
}
|
|
670
|
+
return path;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
#directory(requested: string): string {
|
|
674
|
+
return exportOutputDirectory({
|
|
675
|
+
environment: this.#environment,
|
|
676
|
+
workspaceRoot: this.#workspaceRoot,
|
|
677
|
+
requested,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Sealed under the subject's own key, so destroying that key also takes the
|
|
683
|
+
* account identifier out of the events that record the erasure; what is left
|
|
684
|
+
* proves an erasure happened at a time, which is the point of keeping them.
|
|
685
|
+
* Once the key is gone the subject is named by its marker instead, because a
|
|
686
|
+
* new key would put the account back in the clear.
|
|
687
|
+
*/
|
|
688
|
+
async #event(
|
|
689
|
+
request: ErasureRequest,
|
|
690
|
+
subject: string,
|
|
691
|
+
action: string,
|
|
692
|
+
occurredAt: number,
|
|
693
|
+
metadata: Readonly<Record<string, unknown>>,
|
|
694
|
+
keyDestroyed = false,
|
|
695
|
+
): Promise<void> {
|
|
696
|
+
await this.#repository.appendAuditEvent({
|
|
697
|
+
tenantId: request.tenantId,
|
|
698
|
+
actorId: keyDestroyed ? 'audit.core' : request.operator,
|
|
699
|
+
action,
|
|
700
|
+
subjectType: 'erasure',
|
|
701
|
+
subjectId: keyDestroyed
|
|
702
|
+
? erasureSubjectMarker(request.tenantId, subject)
|
|
703
|
+
: subject,
|
|
704
|
+
metadata,
|
|
705
|
+
occurredAt,
|
|
706
|
+
...(keyDestroyed ? {} : { subjectAccountId: subject }),
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function message(error: unknown): string {
|
|
712
|
+
return error instanceof Error ? error.message : String(error);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function safe(value: string): string {
|
|
716
|
+
return value.replaceAll(/[^A-Za-z0-9_-]/g, '-').slice(0, 64);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Waits for the platform to answer one erasure. The command that recorded the
|
|
721
|
+
* request polls the row it wrote: only the running platform holds every
|
|
722
|
+
* module's erase operation, so a deployment whose platform is down never
|
|
723
|
+
* answers and the wait ends with a stable reason naming the run.
|
|
724
|
+
*/
|
|
725
|
+
export async function awaitErasureRun(
|
|
726
|
+
repository: AuditRepository,
|
|
727
|
+
tenantId: string,
|
|
728
|
+
id: string,
|
|
729
|
+
options: {
|
|
730
|
+
readonly timeoutMs?: number;
|
|
731
|
+
readonly pollMs?: number;
|
|
732
|
+
readonly now?: () => number;
|
|
733
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
734
|
+
} = {},
|
|
735
|
+
): Promise<AuditErasureRun> {
|
|
736
|
+
const now = options.now ?? Date.now;
|
|
737
|
+
const sleep =
|
|
738
|
+
options.sleep ??
|
|
739
|
+
((ms: number) =>
|
|
740
|
+
new Promise<void>((settle) => {
|
|
741
|
+
setTimeout(settle, ms).unref?.();
|
|
742
|
+
}));
|
|
743
|
+
const pollMs = options.pollMs ?? ERASURE_WAIT.pollMs;
|
|
744
|
+
const deadline = now() + (options.timeoutMs ?? ERASURE_WAIT.timeoutMs);
|
|
745
|
+
for (;;) {
|
|
746
|
+
const run = await repository.getErasureRun(tenantId, id);
|
|
747
|
+
if (!run) {
|
|
748
|
+
throw new AuditServiceError(
|
|
749
|
+
'ERASURE_RUN_NOT_FOUND',
|
|
750
|
+
`The erasure run ${id} is no longer recorded in this workspace.`,
|
|
751
|
+
404,
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
if (run.status !== 'requested') return run;
|
|
755
|
+
if (now() >= deadline) {
|
|
756
|
+
throw new AuditServiceError(
|
|
757
|
+
'ERASURE_NOT_ANSWERED',
|
|
758
|
+
`The erasure run ${id} is still requested. Only the running platform performs an erasure; start it and read the run in the workspace's erasure history.`,
|
|
759
|
+
504,
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
await sleep(pollMs);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function progressCount(value: unknown): number | null {
|
|
767
|
+
if (value === undefined) return 0;
|
|
768
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return null;
|
|
769
|
+
return Math.max(0, Math.trunc(value));
|
|
770
|
+
}
|