@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.
- 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 +130 -3
- 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/cli-extension/SKILL.md +1 -1
- package/.ai/skills/deploy-operate/SKILL.md +119 -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/0005_approvals_grant_audit.down.sql +4 -0
- package/modules/approvals/migrations/0005_approvals_grant_audit.up.sql +20 -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 +327 -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 +107 -0
- package/modules/approvals/src/domain/grant.ts +69 -0
- package/modules/approvals/src/domain/types.ts +168 -0
- package/modules/approvals/src/index.ts +61 -0
- package/modules/approvals/src/platform.ts +136 -0
- package/modules/approvals/src/server/index.ts +42 -0
- package/modules/approvals/src/server/runtime.ts +185 -0
- package/modules/approvals/src/services/approvals-service.ts +637 -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 +783 -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 +256 -0
- package/modules/approvals/src/services/notifications.ts +95 -0
- package/modules/approvals/src/services/repository.ts +169 -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/0003_connectors_rotation_inventory.down.sql +2 -0
- package/modules/connectors/migrations/0003_connectors_rotation_inventory.up.sql +19 -0
- package/modules/connectors/migrations/README.md +3 -0
- package/modules/connectors/module.json +32 -0
- package/modules/{automations-workflows-integration → connectors}/package.json +28 -26
- package/modules/connectors/spec/module.yaml +448 -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/cli/commands.json +17 -0
- package/modules/connectors/src/cli/index.ts +126 -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-rotation.ts +221 -0
- package/modules/connectors/src/services/credential-vault.ts +194 -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 +202 -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/0003_documents_rotation_inventory.down.sql +2 -0
- package/modules/documents/migrations/0003_documents_rotation_inventory.up.sql +18 -0
- package/modules/documents/migrations/README.md +3 -0
- package/modules/documents/module.json +32 -0
- package/modules/documents/package.json +50 -0
- package/modules/documents/spec/module.yaml +248 -0
- package/modules/documents/src/acl/permissions.ts +6 -0
- package/modules/documents/src/api/endpoints.ts +401 -0
- package/modules/documents/src/cli/commands.json +17 -0
- package/modules/documents/src/cli/index.ts +145 -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 +270 -0
- package/modules/documents/src/services/documents-service.ts +590 -0
- package/modules/documents/src/services/index.ts +18 -0
- package/modules/documents/src/services/migration.ts +117 -0
- package/modules/documents/src/services/repository.ts +58 -0
- package/modules/documents/src/services/storage-rotation.ts +157 -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/0003_exports_rotation_inventory.down.sql +1 -0
- package/modules/exports/migrations/0003_exports_rotation_inventory.up.sql +9 -0
- package/modules/exports/migrations/README.md +3 -0
- package/modules/exports/module.json +32 -0
- package/modules/exports/package.json +45 -0
- package/modules/exports/spec/module.yaml +230 -0
- package/modules/exports/src/acl/permissions.ts +6 -0
- package/modules/exports/src/api/endpoints.ts +211 -0
- package/modules/exports/src/cli/commands.json +17 -0
- package/modules/exports/src/cli/index.ts +145 -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 +50 -0
- package/modules/exports/src/server/runtime.ts +160 -0
- package/modules/exports/src/services/data-classes.ts +59 -0
- package/modules/exports/src/services/database-repository.ts +403 -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 +116 -0
- package/modules/exports/src/services/repository.ts +96 -0
- package/modules/exports/src/services/storage-rotation.ts +138 -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 +180 -0
- package/packages/database/src/decoders.ts +25 -0
- package/packages/database/src/index.ts +17 -0
- package/packages/database/src/migrations.ts +7 -0
- package/packages/database/src/record-history.ts +5 -14
- package/packages/harness/src/index.ts +2 -0
- package/packages/harness/src/runtime.ts +378 -2
- package/packages/harness/src/tool-adapters.ts +20 -9
- package/packages/kernel/src/acl.ts +179 -0
- package/packages/kernel/src/approval-grant.ts +310 -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 +85 -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 +238 -0
- package/packages/storage/src/index.ts +49 -0
- package/packages/storage/src/local.ts +75 -0
- package/packages/storage/src/port.ts +262 -0
- package/packages/storage/src/read-token.ts +130 -0
- package/packages/storage/src/reseal.ts +128 -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,872 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
currentTraceParent,
|
|
4
|
+
resumeJobTrace,
|
|
5
|
+
serverTracer,
|
|
6
|
+
type Tracer,
|
|
7
|
+
} from '@flowdular/sdk/server';
|
|
8
|
+
import type { AuthPrincipal } from '@flowdular/sdk/modules/auth';
|
|
9
|
+
import { readCsv, CsvError, type CsvDocument } from '../domain/csv.ts';
|
|
10
|
+
import {
|
|
11
|
+
IMPORT_MODES,
|
|
12
|
+
type ImportField,
|
|
13
|
+
type ImportFieldType,
|
|
14
|
+
type ImportMode,
|
|
15
|
+
type ImportRow,
|
|
16
|
+
type ImportValidation,
|
|
17
|
+
type ImportWriteOutcome,
|
|
18
|
+
} from '../domain/ports.ts';
|
|
19
|
+
import {
|
|
20
|
+
IMPORT_CSV_CONTENT_TYPE,
|
|
21
|
+
IMPORT_LIMITS,
|
|
22
|
+
IMPORT_MAX_CSV_BYTES,
|
|
23
|
+
IMPORT_UNEXPECTED_FAILURE,
|
|
24
|
+
type ClaimedImportJob,
|
|
25
|
+
type ImportJob,
|
|
26
|
+
type ImportJobRow,
|
|
27
|
+
type ImportJobStatus,
|
|
28
|
+
type ImportMapping,
|
|
29
|
+
type ImportRequester,
|
|
30
|
+
type ImportRowOutcome,
|
|
31
|
+
} from '../domain/types.ts';
|
|
32
|
+
import { ImportSourceError, type ImportCsvSource } from './csv-source.ts';
|
|
33
|
+
import type {
|
|
34
|
+
ImportPortRegistry,
|
|
35
|
+
RegisteredImportPort,
|
|
36
|
+
} from './port-registry.ts';
|
|
37
|
+
import type {
|
|
38
|
+
ImportJobPage,
|
|
39
|
+
ImportJobQuery,
|
|
40
|
+
ImportRepository,
|
|
41
|
+
ImportRowPage,
|
|
42
|
+
} from './repository.ts';
|
|
43
|
+
|
|
44
|
+
export class ImportServiceError extends Error {
|
|
45
|
+
constructor(
|
|
46
|
+
readonly code: string,
|
|
47
|
+
message: string,
|
|
48
|
+
readonly status = 400,
|
|
49
|
+
) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = 'ImportServiceError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** One target as a screen sees it, with whether this reader may use it. */
|
|
56
|
+
export interface ImportTargetView {
|
|
57
|
+
readonly target: string;
|
|
58
|
+
readonly moduleId: string;
|
|
59
|
+
readonly label: string;
|
|
60
|
+
readonly permission: string;
|
|
61
|
+
readonly permitted: boolean;
|
|
62
|
+
readonly fields: readonly ImportField[];
|
|
63
|
+
readonly naturalKey: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface StartImportInput {
|
|
67
|
+
readonly target: string;
|
|
68
|
+
readonly documentId: string;
|
|
69
|
+
readonly documentRef: string;
|
|
70
|
+
readonly mode: ImportMode;
|
|
71
|
+
readonly dryRun: boolean;
|
|
72
|
+
readonly columns: Readonly<Record<string, string>>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ImportServiceOptions {
|
|
76
|
+
readonly repository: ImportRepository;
|
|
77
|
+
readonly ports: ImportPortRegistry;
|
|
78
|
+
readonly source: ImportCsvSource;
|
|
79
|
+
readonly maxRows: () => number;
|
|
80
|
+
readonly batchSize: () => number;
|
|
81
|
+
readonly now?: () => number;
|
|
82
|
+
readonly newId?: () => string;
|
|
83
|
+
/** Defaults to the process tracer, which is the one `context.tracer` carries. */
|
|
84
|
+
readonly tracer?: Tracer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** One stage of one claimed job, in the trace that enqueued it. */
|
|
88
|
+
const IMPORT_PERFORM_SPAN = 'import.core perform';
|
|
89
|
+
|
|
90
|
+
const EMAIL = /^[^\s@]{1,64}@[^\s@.]{1,63}(\.[^\s@.]{1,63})+$/;
|
|
91
|
+
const INTEGER = /^-?\d{1,15}$/;
|
|
92
|
+
const DATE = /^\d{4}-\d{2}-\d{2}([T ][\d:.+\-Z]{1,24})?$/;
|
|
93
|
+
const BOOLEAN = new Set(['true', 'false', 'yes', 'no', '1', '0']);
|
|
94
|
+
|
|
95
|
+
function shapeOf(value: string, type: ImportFieldType): boolean {
|
|
96
|
+
switch (type) {
|
|
97
|
+
case 'integer':
|
|
98
|
+
return INTEGER.test(value) && Number.isSafeInteger(Number(value));
|
|
99
|
+
case 'boolean':
|
|
100
|
+
return BOOLEAN.has(value.toLowerCase());
|
|
101
|
+
case 'date':
|
|
102
|
+
return DATE.test(value) && Number.isFinite(Date.parse(value));
|
|
103
|
+
case 'email':
|
|
104
|
+
return value.length <= 254 && EMAIL.test(value);
|
|
105
|
+
default:
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function requesterOf(principal: AuthPrincipal): ImportRequester {
|
|
111
|
+
const requester: ImportRequester = {
|
|
112
|
+
accountId: principal.accountId,
|
|
113
|
+
email: principal.email,
|
|
114
|
+
displayName: principal.displayName,
|
|
115
|
+
role: principal.role,
|
|
116
|
+
scopes: [...principal.scopes],
|
|
117
|
+
};
|
|
118
|
+
/* The job row bounds the snapshot at 8 KB. A principal carrying more than
|
|
119
|
+
that is refused here with a code the screen can read, rather than by the
|
|
120
|
+
column check once the job is half written. */
|
|
121
|
+
if (JSON.stringify(requester).length > IMPORT_LIMITS.json) {
|
|
122
|
+
throw new ImportServiceError(
|
|
123
|
+
'REQUESTER_TOO_LARGE',
|
|
124
|
+
'Your account carries more grants than an import job can record.',
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return requester;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The principal the port sees in a background stage. It carries the identity
|
|
132
|
+
* and the grants the requester held when the job was started; the workspace is
|
|
133
|
+
* the job's own, and other workspace memberships are never part of an import.
|
|
134
|
+
*/
|
|
135
|
+
function principalOf(job: ImportJob): AuthPrincipal {
|
|
136
|
+
return {
|
|
137
|
+
accountId: job.requester.accountId,
|
|
138
|
+
tenantId: job.tenantId,
|
|
139
|
+
email: job.requester.email,
|
|
140
|
+
displayName: job.requester.displayName,
|
|
141
|
+
role: job.requester.role,
|
|
142
|
+
scopes: job.requester.scopes,
|
|
143
|
+
tenants: [],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* The comparison a port that folds nothing gets: the key fields exactly as the
|
|
148
|
+
file carries them, length prefixed so two fields cannot run into one another. */
|
|
149
|
+
function exactKey(key: readonly string[], values: ImportRow['values']): string {
|
|
150
|
+
let composed = '';
|
|
151
|
+
for (const field of key) {
|
|
152
|
+
const value = values[field] ?? '';
|
|
153
|
+
composed += `${value.length}:${value}`;
|
|
154
|
+
}
|
|
155
|
+
return composed;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function truncate(value: string | undefined, max: number): string | null {
|
|
159
|
+
if (value === undefined) return null;
|
|
160
|
+
const normalized = value.trim();
|
|
161
|
+
if (normalized === '') return null;
|
|
162
|
+
return normalized.length > max ? normalized.slice(0, max) : normalized;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class ImportService {
|
|
166
|
+
readonly #repository: ImportRepository;
|
|
167
|
+
readonly #ports: ImportPortRegistry;
|
|
168
|
+
readonly #source: ImportCsvSource;
|
|
169
|
+
readonly #maxRows: () => number;
|
|
170
|
+
readonly #batchSize: () => number;
|
|
171
|
+
readonly #now: () => number;
|
|
172
|
+
readonly #newId: () => string;
|
|
173
|
+
readonly #tracer: Tracer;
|
|
174
|
+
|
|
175
|
+
constructor(options: ImportServiceOptions) {
|
|
176
|
+
this.#repository = options.repository;
|
|
177
|
+
this.#ports = options.ports;
|
|
178
|
+
this.#source = options.source;
|
|
179
|
+
this.#maxRows = options.maxRows;
|
|
180
|
+
this.#batchSize = options.batchSize;
|
|
181
|
+
this.#now = options.now ?? (() => Date.now());
|
|
182
|
+
this.#newId = options.newId ?? (() => randomUUID());
|
|
183
|
+
this.#tracer = options.tracer ?? serverTracer();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
targets(principal: AuthPrincipal): readonly ImportTargetView[] {
|
|
187
|
+
return this.#ports.list().map((registered) => ({
|
|
188
|
+
target: registered.target,
|
|
189
|
+
moduleId: registered.moduleId,
|
|
190
|
+
label: registered.port.label,
|
|
191
|
+
permission: registered.port.permission,
|
|
192
|
+
permitted: principal.scopes.includes(registered.port.permission),
|
|
193
|
+
fields: registered.port.fields,
|
|
194
|
+
naturalKey: registered.port.naturalKey,
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async start(
|
|
199
|
+
principal: AuthPrincipal,
|
|
200
|
+
input: StartImportInput,
|
|
201
|
+
): Promise<ImportJob> {
|
|
202
|
+
const registered = this.#require(input.target);
|
|
203
|
+
/* The target module's own permission, checked on the acting principal
|
|
204
|
+
before anything is recorded, so an import never widens what a member
|
|
205
|
+
may write. */
|
|
206
|
+
if (!principal.scopes.includes(registered.port.permission)) {
|
|
207
|
+
throw new ImportServiceError(
|
|
208
|
+
'TARGET_FORBIDDEN',
|
|
209
|
+
'You do not hold the permission this import target requires.',
|
|
210
|
+
403,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (!(IMPORT_MODES as readonly string[]).includes(input.mode)) {
|
|
214
|
+
throw new ImportServiceError(
|
|
215
|
+
'MODE_UNKNOWN',
|
|
216
|
+
'The import mode is unknown.',
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
this.#assertMapping(registered, input.columns);
|
|
220
|
+
const requester = requesterOf(principal);
|
|
221
|
+
|
|
222
|
+
/* Every bound the metadata can answer is answered here, so an oversized or
|
|
223
|
+
non-CSV document never becomes a job at all. */
|
|
224
|
+
const document = await this.#source.describe(
|
|
225
|
+
principal.tenantId,
|
|
226
|
+
input.documentRef,
|
|
227
|
+
input.documentId,
|
|
228
|
+
);
|
|
229
|
+
if (!document || document.status === 'deleted') {
|
|
230
|
+
throw new ImportServiceError(
|
|
231
|
+
'SOURCE_NOT_FOUND',
|
|
232
|
+
'The source document is not stored in this workspace.',
|
|
233
|
+
404,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (document.scan === 'infected') {
|
|
237
|
+
throw new ImportServiceError(
|
|
238
|
+
'SOURCE_INFECTED',
|
|
239
|
+
'The source document was refused by the malware scanner.',
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
if (document.contentType !== IMPORT_CSV_CONTENT_TYPE) {
|
|
243
|
+
throw new ImportServiceError(
|
|
244
|
+
'SOURCE_NOT_CSV',
|
|
245
|
+
'The source document is not a CSV file.',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
if (document.bytes > IMPORT_MAX_CSV_BYTES) {
|
|
249
|
+
throw new ImportServiceError(
|
|
250
|
+
'SOURCE_TOO_LARGE',
|
|
251
|
+
`The source document is larger than ${IMPORT_MAX_CSV_BYTES} bytes.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return this.#repository.createJob({
|
|
256
|
+
id: this.#newId(),
|
|
257
|
+
tenantId: principal.tenantId,
|
|
258
|
+
target: registered.target,
|
|
259
|
+
documentId: input.documentId,
|
|
260
|
+
documentRef: input.documentRef,
|
|
261
|
+
mode: input.mode,
|
|
262
|
+
dryRun: input.dryRun,
|
|
263
|
+
validOnly: false,
|
|
264
|
+
status: 'parsing',
|
|
265
|
+
totalRows: 0,
|
|
266
|
+
validRows: 0,
|
|
267
|
+
writtenRows: 0,
|
|
268
|
+
failedRows: 0,
|
|
269
|
+
requesterAccountId: principal.accountId,
|
|
270
|
+
requester,
|
|
271
|
+
columns: { ...input.columns },
|
|
272
|
+
failureCode: null,
|
|
273
|
+
claimedAt: null,
|
|
274
|
+
startedAt: this.#now(),
|
|
275
|
+
completedAt: null,
|
|
276
|
+
/* Captured here, where the request that asked for the import is still
|
|
277
|
+
the ambient trace; the poll that claims the job runs in none. */
|
|
278
|
+
traceparent: currentTraceParent(),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async continue(
|
|
283
|
+
principal: AuthPrincipal,
|
|
284
|
+
id: string,
|
|
285
|
+
validOnly: boolean,
|
|
286
|
+
): Promise<ImportJob> {
|
|
287
|
+
const job = await this.#requireJob(principal.tenantId, id);
|
|
288
|
+
if (job.dryRun) {
|
|
289
|
+
throw new ImportServiceError(
|
|
290
|
+
'JOB_DRY_RUN',
|
|
291
|
+
'A dry run writes nothing; start a new job to write these rows.',
|
|
292
|
+
409,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (job.status !== 'validated') {
|
|
296
|
+
throw new ImportServiceError(
|
|
297
|
+
'JOB_NOT_VALIDATED',
|
|
298
|
+
'Only a validated job can be continued.',
|
|
299
|
+
409,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
/* The grant is re-checked on the live principal: a job validated before a
|
|
303
|
+
permission was revoked must not write afterwards. */
|
|
304
|
+
const registered = this.#require(job.target);
|
|
305
|
+
if (!principal.scopes.includes(registered.port.permission)) {
|
|
306
|
+
throw new ImportServiceError(
|
|
307
|
+
'TARGET_FORBIDDEN',
|
|
308
|
+
'You do not hold the permission this import target requires.',
|
|
309
|
+
403,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (!validOnly && job.validRows !== job.totalRows) {
|
|
313
|
+
throw new ImportServiceError(
|
|
314
|
+
'VALIDATION_FAILED',
|
|
315
|
+
'This job has invalid rows; continue with the valid rows only.',
|
|
316
|
+
409,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const moved = await this.#repository.continueJob(
|
|
320
|
+
principal.tenantId,
|
|
321
|
+
id,
|
|
322
|
+
validOnly,
|
|
323
|
+
);
|
|
324
|
+
if (!moved) {
|
|
325
|
+
throw new ImportServiceError(
|
|
326
|
+
'JOB_NOT_VALIDATED',
|
|
327
|
+
'Only a validated job can be continued.',
|
|
328
|
+
409,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return moved;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async cancel(principal: AuthPrincipal, id: string): Promise<ImportJob> {
|
|
335
|
+
const job = await this.#requireJob(principal.tenantId, id);
|
|
336
|
+
const cancelled = await this.#repository.advanceJob(
|
|
337
|
+
principal.tenantId,
|
|
338
|
+
id,
|
|
339
|
+
['validated'],
|
|
340
|
+
{
|
|
341
|
+
status: 'cancelled',
|
|
342
|
+
totalRows: job.totalRows,
|
|
343
|
+
validRows: job.validRows,
|
|
344
|
+
writtenRows: job.writtenRows,
|
|
345
|
+
failedRows: job.failedRows,
|
|
346
|
+
failureCode: null,
|
|
347
|
+
completedAt: this.#now(),
|
|
348
|
+
},
|
|
349
|
+
);
|
|
350
|
+
if (!cancelled) {
|
|
351
|
+
throw new ImportServiceError(
|
|
352
|
+
'JOB_NOT_VALIDATED',
|
|
353
|
+
'Only a validated job can be cancelled.',
|
|
354
|
+
409,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return cancelled;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async job(tenantId: string, id: string): Promise<ImportJob> {
|
|
361
|
+
return this.#requireJob(tenantId, id);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async jobs(tenantId: string, query: ImportJobQuery): Promise<ImportJobPage> {
|
|
365
|
+
return this.#repository.listJobs(tenantId, query);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async rows(
|
|
369
|
+
tenantId: string,
|
|
370
|
+
jobId: string,
|
|
371
|
+
limit: number,
|
|
372
|
+
after?: number,
|
|
373
|
+
): Promise<ImportRowPage> {
|
|
374
|
+
await this.#requireJob(tenantId, jobId);
|
|
375
|
+
return this.#repository.listJobRows(tenantId, jobId, limit, after);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async mapping(
|
|
379
|
+
tenantId: string,
|
|
380
|
+
target: string,
|
|
381
|
+
): Promise<ImportMapping | null> {
|
|
382
|
+
this.#require(target);
|
|
383
|
+
return this.#repository.findMapping(tenantId, target);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async saveMapping(
|
|
387
|
+
principal: AuthPrincipal,
|
|
388
|
+
target: string,
|
|
389
|
+
columns: Readonly<Record<string, string>>,
|
|
390
|
+
): Promise<ImportMapping> {
|
|
391
|
+
const registered = this.#require(target);
|
|
392
|
+
if (!principal.scopes.includes(registered.port.permission)) {
|
|
393
|
+
throw new ImportServiceError(
|
|
394
|
+
'TARGET_FORBIDDEN',
|
|
395
|
+
'You do not hold the permission this import target requires.',
|
|
396
|
+
403,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
this.#assertMapping(registered, columns);
|
|
400
|
+
const existing = await this.#repository.findMapping(
|
|
401
|
+
principal.tenantId,
|
|
402
|
+
target,
|
|
403
|
+
);
|
|
404
|
+
return this.#repository.saveMapping({
|
|
405
|
+
id: existing?.id ?? this.#newId(),
|
|
406
|
+
tenantId: principal.tenantId,
|
|
407
|
+
target,
|
|
408
|
+
columns: { ...columns },
|
|
409
|
+
updatedAt: this.#now(),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* One stage of one claimed job. It never throws: a failure is the job's
|
|
415
|
+
* recorded outcome, because the operator reads the job, not this process's
|
|
416
|
+
* log.
|
|
417
|
+
*
|
|
418
|
+
* The stage runs in the trace the request that enqueued the job was part
|
|
419
|
+
* of, from the `traceparent` that request stored on the row, so the spans
|
|
420
|
+
* and the log lines of the background work carry the trace id of the
|
|
421
|
+
* import that asked for it.
|
|
422
|
+
*/
|
|
423
|
+
async perform(
|
|
424
|
+
job: ClaimedImportJob,
|
|
425
|
+
signal?: AbortSignal,
|
|
426
|
+
): Promise<ImportJob> {
|
|
427
|
+
return resumeJobTrace(
|
|
428
|
+
this.#tracer,
|
|
429
|
+
job.traceparent,
|
|
430
|
+
IMPORT_PERFORM_SPAN,
|
|
431
|
+
() => this.#perform(job, signal),
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async #perform(
|
|
436
|
+
job: ClaimedImportJob,
|
|
437
|
+
signal?: AbortSignal,
|
|
438
|
+
): Promise<ImportJob> {
|
|
439
|
+
try {
|
|
440
|
+
return job.status === 'parsing'
|
|
441
|
+
? await this.#parseAndValidate(job, signal)
|
|
442
|
+
: await this.#write(job, signal);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
/* The lease lapsed and another loop reclaimed the job. It is that loop's
|
|
445
|
+
work now, so this stage records nothing at all: failing the job here
|
|
446
|
+
would settle work that is still running. */
|
|
447
|
+
if (error instanceof ImportServiceError && error.code === 'CLAIM_LOST') {
|
|
448
|
+
return (await this.#repository.findJob(job.tenantId, job.id)) ?? job;
|
|
449
|
+
}
|
|
450
|
+
const code =
|
|
451
|
+
error instanceof CsvError || error instanceof ImportSourceError
|
|
452
|
+
? error.code
|
|
453
|
+
: error instanceof ImportServiceError
|
|
454
|
+
? error.code
|
|
455
|
+
: IMPORT_UNEXPECTED_FAILURE;
|
|
456
|
+
return (
|
|
457
|
+
(await this.#repository.advanceJob(job.tenantId, job.id, [job.status], {
|
|
458
|
+
status: 'failed',
|
|
459
|
+
totalRows: job.totalRows,
|
|
460
|
+
validRows: job.validRows,
|
|
461
|
+
writtenRows: job.writtenRows,
|
|
462
|
+
failedRows: job.failedRows,
|
|
463
|
+
failureCode: code.slice(0, IMPORT_LIMITS.failureCode),
|
|
464
|
+
completedAt: this.#now(),
|
|
465
|
+
})) ?? job
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async #parseAndValidate(
|
|
471
|
+
job: ClaimedImportJob,
|
|
472
|
+
signal: AbortSignal | undefined,
|
|
473
|
+
): Promise<ImportJob> {
|
|
474
|
+
const registered = this.#require(job.target);
|
|
475
|
+
const document = await this.#read(job);
|
|
476
|
+
const { rows, invalid } = this.#map(registered, job, document);
|
|
477
|
+
|
|
478
|
+
const verdicts = new Map<number, ImportValidation>();
|
|
479
|
+
for (const entry of invalid) verdicts.set(entry.row, entry);
|
|
480
|
+
for (const entry of this.#duplicates(registered, rows)) {
|
|
481
|
+
verdicts.set(entry.row, entry);
|
|
482
|
+
}
|
|
483
|
+
/* A row already refused is never offered to the port: the port answers
|
|
484
|
+
refusals for rows it was given, and a duplicate is refused here. */
|
|
485
|
+
const pending = rows.filter((row) => !verdicts.has(row.row));
|
|
486
|
+
|
|
487
|
+
const batch = this.#batchSize();
|
|
488
|
+
const principal = principalOf(job);
|
|
489
|
+
for (let start = 0; start < pending.length; start += batch) {
|
|
490
|
+
const slice = pending.slice(start, start + batch);
|
|
491
|
+
for (const verdict of await this.#validate(registered, {
|
|
492
|
+
tenantId: job.tenantId,
|
|
493
|
+
principal,
|
|
494
|
+
rows: slice,
|
|
495
|
+
})) {
|
|
496
|
+
if (verdict.verdict === 'invalid') verdicts.set(verdict.row, verdict);
|
|
497
|
+
}
|
|
498
|
+
this.#checkpoint(signal);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const outcomes: ImportJobRow[] = [];
|
|
502
|
+
const total = document.rows.length;
|
|
503
|
+
let validRows = 0;
|
|
504
|
+
for (let row = 1; row <= total; row += 1) {
|
|
505
|
+
const verdict = verdicts.get(row);
|
|
506
|
+
if (!verdict) validRows += 1;
|
|
507
|
+
outcomes.push(
|
|
508
|
+
this.#outcomeRow(job, row, verdict ? 'invalid' : 'valid', {
|
|
509
|
+
field: verdict?.field,
|
|
510
|
+
reason: verdict?.reason,
|
|
511
|
+
}),
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
await this.#repository.replaceJobRows(job.tenantId, job.id, outcomes);
|
|
515
|
+
|
|
516
|
+
const failed = total - validRows;
|
|
517
|
+
const completed = job.dryRun;
|
|
518
|
+
return (
|
|
519
|
+
(await this.#repository.advanceJob(job.tenantId, job.id, ['parsing'], {
|
|
520
|
+
status: completed ? 'completed' : 'validated',
|
|
521
|
+
totalRows: total,
|
|
522
|
+
validRows,
|
|
523
|
+
writtenRows: 0,
|
|
524
|
+
failedRows: failed,
|
|
525
|
+
failureCode: null,
|
|
526
|
+
completedAt: completed ? this.#now() : null,
|
|
527
|
+
})) ?? job
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
async #write(
|
|
532
|
+
job: ClaimedImportJob,
|
|
533
|
+
signal: AbortSignal | undefined,
|
|
534
|
+
): Promise<ImportJob> {
|
|
535
|
+
const registered = this.#require(job.target);
|
|
536
|
+
const document = await this.#read(job);
|
|
537
|
+
const { rows } = this.#map(registered, job, document);
|
|
538
|
+
const valid = new Set(
|
|
539
|
+
await this.#repository.listValidRowNumbers(job.tenantId, job.id),
|
|
540
|
+
);
|
|
541
|
+
const writable = rows.filter((row) => valid.has(row.row));
|
|
542
|
+
|
|
543
|
+
const batch = this.#batchSize();
|
|
544
|
+
const principal = principalOf(job);
|
|
545
|
+
for (let start = 0; start < writable.length; start += batch) {
|
|
546
|
+
const slice = writable.slice(start, start + batch);
|
|
547
|
+
const outcomes = await this.#writeBatch(registered, {
|
|
548
|
+
tenantId: job.tenantId,
|
|
549
|
+
principal,
|
|
550
|
+
rows: slice,
|
|
551
|
+
mode: job.mode,
|
|
552
|
+
});
|
|
553
|
+
const byRow = new Map(outcomes.map((entry) => [entry.row, entry]));
|
|
554
|
+
const recorded: ImportJobRow[] = [];
|
|
555
|
+
for (const row of slice) {
|
|
556
|
+
/* A row the port said nothing about is recorded as failed: a dropped
|
|
557
|
+
row has to be visible in the job rather than counted as written. */
|
|
558
|
+
const outcome = byRow.get(row.row) ?? {
|
|
559
|
+
row: row.row,
|
|
560
|
+
outcome: 'failed' as const,
|
|
561
|
+
reason: 'PORT_SILENT',
|
|
562
|
+
};
|
|
563
|
+
recorded.push(
|
|
564
|
+
this.#outcomeRow(job, row.row, outcome.outcome, {
|
|
565
|
+
reason: outcome.reason,
|
|
566
|
+
recordRef: outcome.recordRef,
|
|
567
|
+
}),
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
await this.#repository.recordJobRows(job.tenantId, recorded);
|
|
571
|
+
this.#checkpoint(signal);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* The counts are read back from the outcomes rather than counted in this
|
|
575
|
+
pass: a job reclaimed after a crash writes only the rows the earlier
|
|
576
|
+
pass had not reached, and an in-memory counter would report those alone.
|
|
577
|
+
A skipped row is neither written nor failed: the natural key already
|
|
578
|
+
held it, so a skip-existing repeat reports zero written. */
|
|
579
|
+
const counts = await this.#repository.countRowOutcomes(
|
|
580
|
+
job.tenantId,
|
|
581
|
+
job.id,
|
|
582
|
+
);
|
|
583
|
+
return (
|
|
584
|
+
(await this.#repository.advanceJob(job.tenantId, job.id, ['writing'], {
|
|
585
|
+
status: 'completed',
|
|
586
|
+
totalRows: job.totalRows,
|
|
587
|
+
validRows: job.validRows,
|
|
588
|
+
writtenRows: counts.created + counts.updated,
|
|
589
|
+
failedRows: counts.invalid + counts.failed,
|
|
590
|
+
failureCode: null,
|
|
591
|
+
completedAt: this.#now(),
|
|
592
|
+
})) ?? job
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* The claim fence, read once per batch. The runner renews the claim on its
|
|
598
|
+
* own timer so a job longer than the lease is not taken up a second time, and
|
|
599
|
+
* aborts this signal when a renewal matches nothing: the lease lapsed and
|
|
600
|
+
* another loop reclaimed the job. The stage stops there rather than writing
|
|
601
|
+
* on behalf of a claim it no longer has.
|
|
602
|
+
*/
|
|
603
|
+
#checkpoint(signal: AbortSignal | undefined): void {
|
|
604
|
+
if (!signal?.aborted) return;
|
|
605
|
+
throw new ImportServiceError(
|
|
606
|
+
'CLAIM_LOST',
|
|
607
|
+
'Another process took this import job over while it was running.',
|
|
608
|
+
409,
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Rows repeating an earlier row's natural key, refused before batching. A
|
|
614
|
+
* port carries no state between calls, so a repeat in a later batch would
|
|
615
|
+
* otherwise meet its own earlier row as an existing record and be written
|
|
616
|
+
* twice or refused as somebody else's. Which two values are one key is the
|
|
617
|
+
* port's own decision: it folds them in `naturalKeyOf`, and without one they
|
|
618
|
+
* are compared exactly.
|
|
619
|
+
*/
|
|
620
|
+
#duplicates(
|
|
621
|
+
registered: RegisteredImportPort,
|
|
622
|
+
rows: readonly ImportRow[],
|
|
623
|
+
): readonly ImportValidation[] {
|
|
624
|
+
const port = registered.port;
|
|
625
|
+
const key = port.naturalKey;
|
|
626
|
+
const first = key[0];
|
|
627
|
+
if (first === undefined) return [];
|
|
628
|
+
const seen = new Set<string>();
|
|
629
|
+
const duplicates: ImportValidation[] = [];
|
|
630
|
+
for (const row of rows) {
|
|
631
|
+
let composed: string;
|
|
632
|
+
try {
|
|
633
|
+
composed = port.naturalKeyOf?.(row.values) ?? exactKey(key, row.values);
|
|
634
|
+
} catch {
|
|
635
|
+
/* The fold is another module's code: a throwing one costs this file
|
|
636
|
+
the port's own comparison, never the duplicate check itself. */
|
|
637
|
+
composed = exactKey(key, row.values);
|
|
638
|
+
}
|
|
639
|
+
if (seen.has(composed)) {
|
|
640
|
+
duplicates.push({
|
|
641
|
+
row: row.row,
|
|
642
|
+
verdict: 'invalid',
|
|
643
|
+
field: first,
|
|
644
|
+
reason: 'NATURAL_KEY_DUPLICATE',
|
|
645
|
+
});
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
seen.add(composed);
|
|
649
|
+
}
|
|
650
|
+
return duplicates;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/* A port is another module's code. It is isolated so a throwing port fails
|
|
654
|
+
its own batch's rows and never the platform's loop; the rows it was given
|
|
655
|
+
are recorded as failed with a stable reason. */
|
|
656
|
+
async #validate(
|
|
657
|
+
registered: RegisteredImportPort,
|
|
658
|
+
input: Parameters<RegisteredImportPort['port']['validate']>[0],
|
|
659
|
+
): Promise<readonly ImportValidation[]> {
|
|
660
|
+
try {
|
|
661
|
+
return await registered.port.validate(input);
|
|
662
|
+
} catch {
|
|
663
|
+
return input.rows.map((row) => ({
|
|
664
|
+
row: row.row,
|
|
665
|
+
verdict: 'invalid' as const,
|
|
666
|
+
reason: 'PORT_FAILED',
|
|
667
|
+
}));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
async #writeBatch(
|
|
672
|
+
registered: RegisteredImportPort,
|
|
673
|
+
input: Parameters<RegisteredImportPort['port']['write']>[0],
|
|
674
|
+
): Promise<readonly ImportWriteOutcome[]> {
|
|
675
|
+
try {
|
|
676
|
+
return await registered.port.write(input);
|
|
677
|
+
} catch {
|
|
678
|
+
return input.rows.map((row) => ({
|
|
679
|
+
row: row.row,
|
|
680
|
+
outcome: 'failed' as const,
|
|
681
|
+
reason: 'PORT_FAILED',
|
|
682
|
+
}));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async #read(job: ImportJob): Promise<CsvDocument> {
|
|
687
|
+
const body = await this.#source.open(
|
|
688
|
+
job.tenantId,
|
|
689
|
+
job.documentRef,
|
|
690
|
+
job.documentId,
|
|
691
|
+
);
|
|
692
|
+
return readCsv(body, {
|
|
693
|
+
maxBytes: IMPORT_MAX_CSV_BYTES,
|
|
694
|
+
maxRows: this.#maxRows(),
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Maps CSV cells onto port fields. Header positions are resolved once, so
|
|
700
|
+
* mapping is O(rows x fields) and never searches the header per cell.
|
|
701
|
+
*/
|
|
702
|
+
#map(
|
|
703
|
+
registered: RegisteredImportPort,
|
|
704
|
+
job: ImportJob,
|
|
705
|
+
document: CsvDocument,
|
|
706
|
+
): { rows: readonly ImportRow[]; invalid: readonly ImportValidation[] } {
|
|
707
|
+
const position = new Map(
|
|
708
|
+
document.header.map((name, index) => [name, index]),
|
|
709
|
+
);
|
|
710
|
+
const columns: { field: ImportField; index: number }[] = [];
|
|
711
|
+
for (const field of registered.port.fields) {
|
|
712
|
+
const header = job.columns[field.id];
|
|
713
|
+
if (header === undefined) {
|
|
714
|
+
if (field.required) {
|
|
715
|
+
throw new ImportServiceError(
|
|
716
|
+
'MAPPING_COLUMN_MISSING',
|
|
717
|
+
`The file has no column for ${field.id}.`,
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
/* A mapped column the file does not carry fails the job whether the
|
|
723
|
+
field is required or not: skipping it would import the file the
|
|
724
|
+
requester mapped as if that column had been left out on purpose. */
|
|
725
|
+
const index = position.get(header);
|
|
726
|
+
if (index === undefined) {
|
|
727
|
+
throw new ImportServiceError(
|
|
728
|
+
'MAPPING_COLUMN_MISSING',
|
|
729
|
+
`The file has no column ${header} for ${field.id}.`,
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
columns.push({ field, index });
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const rows: ImportRow[] = [];
|
|
736
|
+
const invalid: ImportValidation[] = [];
|
|
737
|
+
for (const record of document.rows) {
|
|
738
|
+
/* A row whose cell count differs from the header is reported rather
|
|
739
|
+
than padded or truncated: silently dropping a trailing cell writes a
|
|
740
|
+
record the file did not describe. */
|
|
741
|
+
if (record.cells.length !== document.header.length) {
|
|
742
|
+
invalid.push({
|
|
743
|
+
row: record.row,
|
|
744
|
+
verdict: 'invalid',
|
|
745
|
+
reason: 'ROW_RAGGED',
|
|
746
|
+
});
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
const values: Record<string, string> = {};
|
|
750
|
+
let refusal: ImportValidation | null = null;
|
|
751
|
+
for (const { field, index } of columns) {
|
|
752
|
+
const cell = record.cells[index] ?? '';
|
|
753
|
+
if (cell === '') {
|
|
754
|
+
if (field.required) {
|
|
755
|
+
refusal = {
|
|
756
|
+
row: record.row,
|
|
757
|
+
verdict: 'invalid',
|
|
758
|
+
field: field.id,
|
|
759
|
+
reason: 'FIELD_REQUIRED',
|
|
760
|
+
};
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (!shapeOf(cell, field.type)) {
|
|
766
|
+
refusal = {
|
|
767
|
+
row: record.row,
|
|
768
|
+
verdict: 'invalid',
|
|
769
|
+
field: field.id,
|
|
770
|
+
reason: 'FIELD_TYPE',
|
|
771
|
+
};
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
values[field.id] = cell;
|
|
775
|
+
}
|
|
776
|
+
if (refusal) invalid.push(refusal);
|
|
777
|
+
else rows.push({ row: record.row, values });
|
|
778
|
+
}
|
|
779
|
+
return { rows, invalid };
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
#outcomeRow(
|
|
783
|
+
job: ImportJob,
|
|
784
|
+
rowNumber: number,
|
|
785
|
+
outcome: ImportRowOutcome,
|
|
786
|
+
detail: {
|
|
787
|
+
readonly field?: string | undefined;
|
|
788
|
+
readonly reason?: string | undefined;
|
|
789
|
+
readonly recordRef?: string | undefined;
|
|
790
|
+
},
|
|
791
|
+
): ImportJobRow {
|
|
792
|
+
return {
|
|
793
|
+
id: this.#newId(),
|
|
794
|
+
tenantId: job.tenantId,
|
|
795
|
+
jobId: job.id,
|
|
796
|
+
rowNumber,
|
|
797
|
+
outcome,
|
|
798
|
+
field: truncate(detail.field, IMPORT_LIMITS.field),
|
|
799
|
+
reason: truncate(detail.reason, IMPORT_LIMITS.reason),
|
|
800
|
+
recordRef: truncate(detail.recordRef, IMPORT_LIMITS.recordRef),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
#assertMapping(
|
|
805
|
+
registered: RegisteredImportPort,
|
|
806
|
+
columns: Readonly<Record<string, string>>,
|
|
807
|
+
): void {
|
|
808
|
+
/* The job row and the saved mapping each bound the serialized object at
|
|
809
|
+
8 KB, and the endpoint's per-entry bounds still allow a mapping past it.
|
|
810
|
+
It is refused here with a code the screen can read, rather than by the
|
|
811
|
+
column check once the job is half written. */
|
|
812
|
+
if (JSON.stringify(columns).length > IMPORT_LIMITS.json) {
|
|
813
|
+
throw new ImportServiceError(
|
|
814
|
+
'MAPPING_TOO_LARGE',
|
|
815
|
+
'The column mapping is larger than an import job can record.',
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
const fields = new Map(
|
|
819
|
+
registered.port.fields.map((field) => [field.id, field]),
|
|
820
|
+
);
|
|
821
|
+
/* One header feeds one field. Two fields reading one column would write
|
|
822
|
+
the same cell under two meanings, which is a mapping mistake rather
|
|
823
|
+
than a choice the file can answer. */
|
|
824
|
+
const headers = new Map<string, string>();
|
|
825
|
+
for (const [id, header] of Object.entries(columns)) {
|
|
826
|
+
if (!fields.has(id)) {
|
|
827
|
+
throw new ImportServiceError(
|
|
828
|
+
'MAPPING_UNKNOWN_FIELD',
|
|
829
|
+
`The target has no field ${id}.`,
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
const taken = headers.get(header);
|
|
833
|
+
if (taken !== undefined) {
|
|
834
|
+
throw new ImportServiceError(
|
|
835
|
+
'MAPPING_COLUMN_REUSED',
|
|
836
|
+
`The column ${header} is mapped to ${taken} and ${id}.`,
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
headers.set(header, id);
|
|
840
|
+
}
|
|
841
|
+
for (const field of registered.port.fields) {
|
|
842
|
+
if (field.required && !columns[field.id]) {
|
|
843
|
+
throw new ImportServiceError(
|
|
844
|
+
'MAPPING_INCOMPLETE',
|
|
845
|
+
`The required field ${field.id} has no column.`,
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
#require(target: string): RegisteredImportPort {
|
|
852
|
+
const registered = this.#ports.find(target);
|
|
853
|
+
if (!registered) {
|
|
854
|
+
throw new ImportServiceError(
|
|
855
|
+
'TARGET_UNKNOWN',
|
|
856
|
+
'No module offers that import target.',
|
|
857
|
+
404,
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
return registered;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
async #requireJob(tenantId: string, id: string): Promise<ImportJob> {
|
|
864
|
+
const job = await this.#repository.findJob(tenantId, id);
|
|
865
|
+
if (!job) {
|
|
866
|
+
throw new ImportServiceError('JOB_NOT_FOUND', 'No such import job.', 404);
|
|
867
|
+
}
|
|
868
|
+
return job;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
export type { ImportJobStatus };
|