@k2b/cloud 0.6.0-rc.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/README.md +98 -0
- package/package.json +128 -0
- package/public/logo.svg +1 -0
- package/scripts/app-favicon.test.ts +49 -0
- package/scripts/app-favicon.ts +51 -0
- package/scripts/build.ts +205 -0
- package/scripts/preload.ts +164 -0
- package/scripts/sync-recovery-smoke.ts +200 -0
- package/src/_internal/app-favicon.ts +2 -0
- package/src/_internal/app-presentation.ts +61 -0
- package/src/_internal/app-workspace-state.ts +24 -0
- package/src/_internal/bounded-json.ts +47 -0
- package/src/_internal/build-metadata.ts +9 -0
- package/src/_internal/capabilities.ts +1118 -0
- package/src/_internal/define-app.ts +801 -0
- package/src/_internal/heartbeat.ts +167 -0
- package/src/_internal/help-catalog.ts +241 -0
- package/src/_internal/help.ts +95 -0
- package/src/_internal/nats-connection.ts +34 -0
- package/src/_internal/page-responses.ts +40 -0
- package/src/_internal/process-sync.ts +117 -0
- package/src/_internal/readiness.ts +3 -0
- package/src/_internal/registry-validation.ts +136 -0
- package/src/_internal/registry-watch.ts +52 -0
- package/src/_internal/registry.ts +338 -0
- package/src/_internal/runtime-compatibility.ts +64 -0
- package/src/_internal/runtime-context.ts +84 -0
- package/src/_internal/runtime-watcher.ts +78 -0
- package/src/_internal/static-assets.ts +86 -0
- package/src/_internal/status-preserving-ssr.ts +49 -0
- package/src/access/PermissionEditor.tsx +486 -0
- package/src/access/ResourceApiKeys.tsx +271 -0
- package/src/access/messages.ts +121 -0
- package/src/access/ui.ts +16 -0
- package/src/account/Avatar.tsx +28 -0
- package/src/account/EntitySearch.tsx +363 -0
- package/src/account/avatar-upload-dialog.tsx +197 -0
- package/src/account/avatar-upload.ts +149 -0
- package/src/account/ui.ts +3 -0
- package/src/ai/admin.ts +47 -0
- package/src/ai/approval-routes.ts +130 -0
- package/src/ai/approvals.ts +139 -0
- package/src/ai/assistant-models.ts +61 -0
- package/src/ai/attachments.ts +40 -0
- package/src/ai/browser.ts +92 -0
- package/src/ai/capabilities.ts +714 -0
- package/src/ai/capability-execution.ts +151 -0
- package/src/ai/chat/blocks.render.test.tsx +1108 -0
- package/src/ai/chat/blocks.tsx +730 -0
- package/src/ai/chat/builtin-tools.tsx +428 -0
- package/src/ai/chat/capability-table.tsx +87 -0
- package/src/ai/chat/composer-adapter.ts +169 -0
- package/src/ai/chat/file-tools.tsx +81 -0
- package/src/ai/chat/message-actions.tsx +370 -0
- package/src/ai/chat/message-utils.ts +479 -0
- package/src/ai/chat/messages.ts +43 -0
- package/src/ai/chat/presentation.tsx +395 -0
- package/src/ai/chat/primitives.tsx +3 -0
- package/src/ai/chat/tool-disclosure.tsx +41 -0
- package/src/ai/chat/turn-navigator-utils.ts +28 -0
- package/src/ai/chat/turn-navigator.tsx +164 -0
- package/src/ai/chat/user-message.tsx +149 -0
- package/src/ai/chat/visual-tools.tsx +435 -0
- package/src/ai/chat/web-tools.tsx +202 -0
- package/src/ai/chat-task-contracts.ts +113 -0
- package/src/ai/chat-tasks.ts +952 -0
- package/src/ai/client/controller.ts +1211 -0
- package/src/ai/client/file-source.ts +95 -0
- package/src/ai/client/live-connection.ts +140 -0
- package/src/ai/client/projection.ts +178 -0
- package/src/ai/client/transport.ts +111 -0
- package/src/ai/compaction.ts +182 -0
- package/src/ai/credentials.ts +124 -0
- package/src/ai/default-tool-contracts.ts +108 -0
- package/src/ai/default-tools.ts +127 -0
- package/src/ai/enrich.ts +280 -0
- package/src/ai/executor.ts +1624 -0
- package/src/ai/fetch-file-tool.ts +313 -0
- package/src/ai/file-context.ts +92 -0
- package/src/ai/file-media-type.ts +28 -0
- package/src/ai/file-mount.ts +16 -0
- package/src/ai/file-tools.ts +566 -0
- package/src/ai/files-store.ts +390 -0
- package/src/ai/firecrawl-tools.ts +305 -0
- package/src/ai/grids-skill.ts +126 -0
- package/src/ai/http.ts +265 -0
- package/src/ai/index.ts +337 -0
- package/src/ai/kit-skill.ts +22 -0
- package/src/ai/limits.ts +5 -0
- package/src/ai/live-events.ts +289 -0
- package/src/ai/live-messages.ts +45 -0
- package/src/ai/live-outbox.ts +102 -0
- package/src/ai/live-routes.ts +412 -0
- package/src/ai/live.ts +2 -0
- package/src/ai/maintenance.ts +302 -0
- package/src/ai/markdown-pdf-tool.ts +95 -0
- package/src/ai/memories.ts +605 -0
- package/src/ai/memory-learning-runs.ts +222 -0
- package/src/ai/memory-learning.ts +619 -0
- package/src/ai/memory-tool.ts +120 -0
- package/src/ai/memory-workflow-evidence.ts +158 -0
- package/src/ai/migrate.ts +2082 -0
- package/src/ai/model-access-migrate.ts +30 -0
- package/src/ai/model-access.ts +191 -0
- package/src/ai/personal-agent.ts +6 -0
- package/src/ai/prefs.ts +72 -0
- package/src/ai/project-tool.ts +94 -0
- package/src/ai/projects-routes.ts +277 -0
- package/src/ai/projects.ts +902 -0
- package/src/ai/protocol.ts +279 -0
- package/src/ai/provider.ts +73 -0
- package/src/ai/public-projection.ts +25 -0
- package/src/ai/resource-markers.ts +31 -0
- package/src/ai/resource-refs.ts +70 -0
- package/src/ai/routes.ts +1131 -0
- package/src/ai/runtime-tools.ts +39 -0
- package/src/ai/runtime.ts +544 -0
- package/src/ai/settings.ts +529 -0
- package/src/ai/short-id.ts +45 -0
- package/src/ai/skill-catalog.ts +73 -0
- package/src/ai/skill-format.ts +145 -0
- package/src/ai/skill-seeds.ts +430 -0
- package/src/ai/skill-tool.ts +81 -0
- package/src/ai/skills-routes.ts +143 -0
- package/src/ai/skills.ts +934 -0
- package/src/ai/solid.ts +27 -0
- package/src/ai/store.ts +3113 -0
- package/src/ai/stream.ts +308 -0
- package/src/ai/structured-runs.ts +66 -0
- package/src/ai/structured.ts +186 -0
- package/src/ai/system-prompt.ts +170 -0
- package/src/ai/task-prompt.ts +22 -0
- package/src/ai/timeline.ts +148 -0
- package/src/ai/tool-audit.ts +177 -0
- package/src/ai/tool-result-budget.ts +16 -0
- package/src/ai/tools.ts +155 -0
- package/src/ai/types.ts +967 -0
- package/src/ai/ui.tsx +39 -0
- package/src/ai/usage-migrate.ts +81 -0
- package/src/ai/usage.ts +315 -0
- package/src/ai/validate.ts +54 -0
- package/src/ai/vision-tool.ts +79 -0
- package/src/api/accounts-entities.ts +174 -0
- package/src/api/admin-ai-projects.ts +74 -0
- package/src/api/admin-ai-skills.ts +76 -0
- package/src/api/admin-ai-usage.ts +42 -0
- package/src/api/admin-core-settings.ts +508 -0
- package/src/api/admin-identity.ts +148 -0
- package/src/api/admin-lifecycle.ts +206 -0
- package/src/api/admin-linux-identities.ts +66 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/app-approval.ts +129 -0
- package/src/api/apps.ts +51 -0
- package/src/api/auth/schemas.ts +54 -0
- package/src/api/auth.ts +370 -0
- package/src/api/capabilities.ts +658 -0
- package/src/api/help-messages.ts +19 -0
- package/src/api/help.ts +78 -0
- package/src/api/index.ts +108 -0
- package/src/api/mcp.ts +744 -0
- package/src/api/me-rail.ts +45 -0
- package/src/api/me.ts +753 -0
- package/src/api/search/schemas.ts +79 -0
- package/src/api/search.bench.ts +185 -0
- package/src/api/search.ts +407 -0
- package/src/api/sync-ops.ts +79 -0
- package/src/api/widgets.ts +143 -0
- package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
- package/src/browser/CloudResourceSearch.tsx +791 -0
- package/src/browser/app-approval-vault.ts +305 -0
- package/src/browser/app-approval.ts +234 -0
- package/src/browser/live-websocket.ts +268 -0
- package/src/browser/locale-preference.ts +25 -0
- package/src/browser/notification-target.ts +27 -0
- package/src/browser/notification-timeout.ts +14 -0
- package/src/browser/notifications.ts +181 -0
- package/src/browser/resource-clipboard.ts +70 -0
- package/src/browser/resource-picker.tsx +39 -0
- package/src/browser/resource-search-messages.ts +55 -0
- package/src/browser/resource-search.ts +27 -0
- package/src/capabilities/client.ts +149 -0
- package/src/capabilities/response.ts +39 -0
- package/src/capabilities/server.ts +215 -0
- package/src/capabilities/signals.ts +4 -0
- package/src/capabilities/testing.ts +12 -0
- package/src/capabilities/types.ts +32 -0
- package/src/cli/access.ts +499 -0
- package/src/cli/account.ts +382 -0
- package/src/cli/admin/account-administration.ts +45 -0
- package/src/cli/admin/account-categories.ts +49 -0
- package/src/cli/admin/ai-usage.ts +118 -0
- package/src/cli/admin/app-sign-in.ts +49 -0
- package/src/cli/admin/data.ts +343 -0
- package/src/cli/admin/documentation.ts +34 -0
- package/src/cli/admin/gateway.ts +155 -0
- package/src/cli/admin/index.ts +61 -0
- package/src/cli/admin/instance.ts +250 -0
- package/src/cli/admin/jobs.ts +223 -0
- package/src/cli/admin/legal.ts +122 -0
- package/src/cli/admin/linux.ts +53 -0
- package/src/cli/admin/logs.ts +324 -0
- package/src/cli/admin/metrics.ts +142 -0
- package/src/cli/admin/nats.ts +167 -0
- package/src/cli/admin/notifications.ts +516 -0
- package/src/cli/admin/shared.ts +113 -0
- package/src/cli/admin/sync.ts +188 -0
- package/src/cli/admin/telemetry.ts +314 -0
- package/src/cli/admin/webhooks.ts +222 -0
- package/src/cli/admin/workflows.ts +367 -0
- package/src/cli/apps.ts +40 -0
- package/src/cli/capabilities.ts +239 -0
- package/src/cli/commands.ts +604 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/locale.ts +31 -0
- package/src/cli/output.ts +58 -0
- package/src/clients/core.ts +20 -0
- package/src/config/env.ts +54 -0
- package/src/config/index.ts +6 -0
- package/src/contracts/account-categories.ts +62 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app-approval.ts +197 -0
- package/src/contracts/app.ts +162 -0
- package/src/contracts/capabilities.ts +484 -0
- package/src/contracts/index.ts +14 -0
- package/src/contracts/notification-live.ts +66 -0
- package/src/contracts/notification-types.ts +235 -0
- package/src/contracts/posix.ts +69 -0
- package/src/contracts/profile.ts +85 -0
- package/src/contracts/rail-preferences.ts +53 -0
- package/src/contracts/registry.ts +110 -0
- package/src/contracts/resource-clipboard.ts +33 -0
- package/src/contracts/settings-types.ts +98 -0
- package/src/contracts/shared.ts +408 -0
- package/src/contracts/user-notifications.ts +101 -0
- package/src/contracts/widgets.ts +183 -0
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/index.ts +58 -0
- package/src/server/actor.ts +41 -0
- package/src/server/api/index.ts +2 -0
- package/src/server/api/respond.ts +96 -0
- package/src/server/api-client.ts +54 -0
- package/src/server/app-context.ts +39 -0
- package/src/server/help.ts +187 -0
- package/src/server/index.ts +89 -0
- package/src/server/locale.ts +55 -0
- package/src/server/middleware/auth.ts +386 -0
- package/src/server/middleware/index.ts +28 -0
- package/src/server/middleware/invocation.ts +55 -0
- package/src/server/middleware/middleware.ts +65 -0
- package/src/server/middleware/openapi.ts +126 -0
- package/src/server/middleware/rate-limit.ts +125 -0
- package/src/server/middleware/request-logger.ts +41 -0
- package/src/server/middleware/route-template.ts +46 -0
- package/src/server/middleware/runtime.ts +22 -0
- package/src/server/middleware/settings.ts +55 -0
- package/src/server/middleware/validator.ts +49 -0
- package/src/server/middleware/workload.ts +22 -0
- package/src/server/ratelimit.ts +151 -0
- package/src/server/request-cookies.ts +18 -0
- package/src/server/services/access.ts +644 -0
- package/src/server/services/freeipa/client.ts +137 -0
- package/src/server/services/freeipa/index.ts +9 -0
- package/src/server/services/freeipa/session.ts +123 -0
- package/src/server/services/freeipa/test-certificates.ts +44 -0
- package/src/server/services/freeipa/tls.ts +44 -0
- package/src/server/services/freeipa/transport.ts +116 -0
- package/src/server/services/freeipa/util.ts +59 -0
- package/src/server/services/geo.ts +198 -0
- package/src/server/services/index.ts +48 -0
- package/src/server/services/pagination.ts +22 -0
- package/src/server/services/services.ts +13 -0
- package/src/server/time.ts +33 -0
- package/src/services/account-category-policy.ts +51 -0
- package/src/services/account-lifecycle/audit.ts +41 -0
- package/src/services/account-lifecycle/index.ts +940 -0
- package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
- package/src/services/account-lifecycle/notification-sender.ts +13 -0
- package/src/services/account-lifecycle/scheduler.ts +506 -0
- package/src/services/account-model.ts +22 -0
- package/src/services/accounts/app.ts +1671 -0
- package/src/services/accounts/authz.ts +44 -0
- package/src/services/accounts/avatar.ts +120 -0
- package/src/services/accounts/base-group.ts +11 -0
- package/src/services/accounts/base-user.ts +46 -0
- package/src/services/accounts/duplicate-emails.ts +82 -0
- package/src/services/accounts/entities.ts +622 -0
- package/src/services/accounts/group-sql.ts +106 -0
- package/src/services/accounts/groups.ts +272 -0
- package/src/services/accounts/index.ts +14 -0
- package/src/services/accounts/ipa-data.ts +66 -0
- package/src/services/accounts/lifecycle.ts +2 -0
- package/src/services/accounts/local-groups.ts +499 -0
- package/src/services/accounts/model.ts +129 -0
- package/src/services/accounts/notification-sender.ts +31 -0
- package/src/services/accounts/posix.ts +386 -0
- package/src/services/accounts/request-policy.ts +11 -0
- package/src/services/accounts/switching.ts +126 -0
- package/src/services/accounts/users.ts +745 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/app-approval.ts +498 -0
- package/src/services/audit/index.ts +438 -0
- package/src/services/auth-flows/index.ts +15 -0
- package/src/services/auth-flows/ipa.ts +197 -0
- package/src/services/auth-flows/magic-link.ts +185 -0
- package/src/services/auth-flows/notification-sender.ts +10 -0
- package/src/services/auth-flows/password-reset.ts +268 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/document-extraction.ts +148 -0
- package/src/services/freeipa-config.ts +179 -0
- package/src/services/gateway.ts +186 -0
- package/src/services/identity/constants.ts +18 -0
- package/src/services/identity/index.ts +52 -0
- package/src/services/identity/invocation-actor.ts +120 -0
- package/src/services/identity/invocation-authority.ts +53 -0
- package/src/services/identity/invocation-operations.ts +15 -0
- package/src/services/identity/invocation-token.ts +276 -0
- package/src/services/identity/key-config.ts +41 -0
- package/src/services/identity/key-ring.ts +530 -0
- package/src/services/identity/metrics.ts +29 -0
- package/src/services/identity/routes.ts +18 -0
- package/src/services/identity/runtime-config.ts +88 -0
- package/src/services/identity/session-token.ts +152 -0
- package/src/services/identity/signer-refresh.ts +44 -0
- package/src/services/identity/workload-auth.ts +64 -0
- package/src/services/index.ts +187 -0
- package/src/services/ipa/auth.ts +216 -0
- package/src/services/ipa/connection.ts +57 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/groups.ts +722 -0
- package/src/services/ipa/guard.ts +17 -0
- package/src/services/ipa/index.ts +17 -0
- package/src/services/ipa/posix.ts +19 -0
- package/src/services/ipa/profile.ts +132 -0
- package/src/services/ipa/runtime.ts +28 -0
- package/src/services/ipa/search.ts +154 -0
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.ts +100 -0
- package/src/services/ipa/sync.ts +862 -0
- package/src/services/ipa/users.ts +806 -0
- package/src/services/legal-consent.ts +76 -0
- package/src/services/logging/index.ts +477 -0
- package/src/services/logging/redaction.ts +20 -0
- package/src/services/logging/trace.ts +1231 -0
- package/src/services/mandates/index.ts +855 -0
- package/src/services/mandates/policy.ts +85 -0
- package/src/services/network-security.ts +31 -0
- package/src/services/notifications/batches.ts +722 -0
- package/src/services/notifications/browser.ts +252 -0
- package/src/services/notifications/catalog.ts +113 -0
- package/src/services/notifications/channels.ts +104 -0
- package/src/services/notifications/dispatcher.ts +139 -0
- package/src/services/notifications/email.ts +118 -0
- package/src/services/notifications/index.ts +633 -0
- package/src/services/notifications/live.ts +50 -0
- package/src/services/notifications/observability.ts +407 -0
- package/src/services/notifications/platform.ts +421 -0
- package/src/services/notifications/runtime.ts +120 -0
- package/src/services/notifications/user.ts +248 -0
- package/src/services/notifications/web-push-transport.ts +135 -0
- package/src/services/oauth-tokens.ts +209 -0
- package/src/services/outbox.ts +187 -0
- package/src/services/pdf/gotenberg.ts +290 -0
- package/src/services/pdf/index.ts +43 -0
- package/src/services/pdf/markdown.ts +209 -0
- package/src/services/pdf/template-preview.ts +122 -0
- package/src/services/postgres.ts +76 -0
- package/src/services/providers/index.ts +27 -0
- package/src/services/providers/local/auth.ts +51 -0
- package/src/services/providers/local/index.ts +4 -0
- package/src/services/providers/local/users.ts +263 -0
- package/src/services/rail-preferences.ts +27 -0
- package/src/services/runtime-lifecycle.ts +191 -0
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.ts +891 -0
- package/src/services/service-accounts.ts +184 -0
- package/src/services/session/index.ts +236 -0
- package/src/services/session/test-fixture.ts +18 -0
- package/src/services/session/user.ts +144 -0
- package/src/services/settings/api.ts +61 -0
- package/src/services/settings/app.ts +86 -0
- package/src/services/settings/core-settings.ts +796 -0
- package/src/services/settings/crypto.ts +69 -0
- package/src/services/settings/defaults.ts +374 -0
- package/src/services/settings/index.ts +215 -0
- package/src/services/settings/namespace.ts +9 -0
- package/src/services/settings/snapshot.ts +49 -0
- package/src/services/settings/store.ts +241 -0
- package/src/services/settings/templates.ts +4 -0
- package/src/services/sync-ops.ts +403 -0
- package/src/services/topic-cursor.ts +22 -0
- package/src/services/weather/forecast.ts +323 -0
- package/src/services/weather/geo.ts +119 -0
- package/src/services/weather/index.ts +67 -0
- package/src/services/weather/location.ts +24 -0
- package/src/services/weather/locations.ts +133 -0
- package/src/services/weather/migrate.ts +45 -0
- package/src/services/weather/settings.ts +66 -0
- package/src/services/weather/short-id.ts +57 -0
- package/src/services/weather/types.ts +61 -0
- package/src/services/weather/ui.ts +50 -0
- package/src/services/webauthn.ts +385 -0
- package/src/shared/account-action-notice.ts +70 -0
- package/src/shared/account-display.ts +15 -0
- package/src/shared/account-session.ts +15 -0
- package/src/shared/ai-platform-prompt.ts +134 -0
- package/src/shared/ai-usage.ts +49 -0
- package/src/shared/app-presentation.ts +48 -0
- package/src/shared/app-url.ts +12 -0
- package/src/shared/branding.ts +14 -0
- package/src/shared/capability-messages.ts +70 -0
- package/src/shared/email-html.ts +54 -0
- package/src/shared/format.ts +91 -0
- package/src/shared/help.ts +68 -0
- package/src/shared/icons.ts +400 -0
- package/src/shared/index.ts +54 -0
- package/src/shared/locale.ts +48 -0
- package/src/shared/login-method.ts +44 -0
- package/src/shared/markdown/client.ts +130 -0
- package/src/shared/markdown/extensions/code.ts +105 -0
- package/src/shared/markdown/extensions/guided-help.ts +100 -0
- package/src/shared/markdown/extensions/images.ts +79 -0
- package/src/shared/markdown/extensions/info-blocks.ts +97 -0
- package/src/shared/markdown/extensions/katex.ts +120 -0
- package/src/shared/markdown/extensions/links.ts +43 -0
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +109 -0
- package/src/shared/markdown/extensions/task-list.ts +53 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +193 -0
- package/src/shared/markdown/shared.ts +36 -0
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/network-address.ts +106 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/template-rendering.ts +140 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +53 -0
- package/src/ssr/AdminSidebar.tsx +50 -0
- package/src/ssr/AppLaunchpad.island.tsx +236 -0
- package/src/ssr/AppWorkspaceController.island.tsx +21 -0
- package/src/ssr/BrowserNotifications.island.tsx +135 -0
- package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
- package/src/ssr/GlobalSearchDialog.tsx +35 -0
- package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
- package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
- package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
- package/src/ssr/Layout.render.test.tsx +148 -0
- package/src/ssr/Layout.tsx +292 -0
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutFooter.tsx +22 -0
- package/src/ssr/LayoutHeader.tsx +81 -0
- package/src/ssr/LayoutHelp.tsx +869 -0
- package/src/ssr/LayoutPreferences.island.tsx +36 -0
- package/src/ssr/LayoutRail.tsx +58 -0
- package/src/ssr/MinimalLayout.tsx +46 -0
- package/src/ssr/PageError.tsx +29 -0
- package/src/ssr/ProfilePreferences.island.tsx +92 -0
- package/src/ssr/RailApps.island.tsx +61 -0
- package/src/ssr/RailEditor.tsx +293 -0
- package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/admin-active-link.ts +47 -0
- package/src/ssr/admin-navigation.ts +78 -0
- package/src/ssr/app-appearance.ts +23 -0
- package/src/ssr/app-navigation.ts +41 -0
- package/src/ssr/help-messages.ts +104 -0
- package/src/ssr/index.ts +14 -0
- package/src/ssr/islands/SearchBar.island.tsx +80 -0
- package/src/ssr/islands/index.ts +16 -0
- package/src/ssr/layout-context.ts +17 -0
- package/src/ssr/layout-help-markdown.ts +9 -0
- package/src/ssr/layout-help-navigation.ts +24 -0
- package/src/ssr/layout-help-url.ts +5 -0
- package/src/ssr/layout-runtime.ts +22 -0
- package/src/ssr/page-error-messages.ts +25 -0
- package/src/ssr/platform-messages.ts +135 -0
- package/src/ssr/preference-controller.ts +44 -0
- package/src/ssr/profile-preferences-messages.ts +29 -0
- package/src/ssr/rail-context.ts +21 -0
- package/src/ssr/rail-messages.ts +66 -0
- package/src/ssr/rail-navigation.ts +60 -0
- package/src/ssr/runtime.ts +31 -0
- package/src/ssr/url-filter.ts +127 -0
- package/src/styles/base-popover.css +36 -0
- package/src/styles/effects.css +851 -0
- package/src/styles/global.css +268 -0
- package/src/styles/input.css +58 -0
- package/src/styles/tokens.css +230 -0
- package/src/styles/utilities-buttons.css +405 -0
- package/src/styles/utilities-data.css +33 -0
- package/src/styles/utilities-feedback.css +232 -0
- package/src/styles/utilities-layout.css +603 -0
- package/src/styles/utilities-markdown-editor.css +525 -0
- package/src/styles/utilities-markdown-table.css +234 -0
- package/src/styles/utilities-navigation.css +1054 -0
- package/src/types/ambient.d.ts +24 -0
- package/src/workflows/ai/index.ts +17 -0
- package/src/workflows/ai/runtime.ts +286 -0
- package/src/workflows/ai/store.ts +314 -0
- package/src/workflows/ai/types.ts +111 -0
- package/src/workflows/ai-actions.ts +269 -0
- package/src/workflows/authoring.ts +88 -0
- package/src/workflows/builtins.ts +193 -0
- package/src/workflows/contracts.ts +280 -0
- package/src/workflows/definition.ts +334 -0
- package/src/workflows/editor.ts +113 -0
- package/src/workflows/index.ts +71 -0
- package/src/workflows/language/binder.ts +45 -0
- package/src/workflows/language/canonical.ts +29 -0
- package/src/workflows/language/compiler.ts +396 -0
- package/src/workflows/language/expressions.ts +34 -0
- package/src/workflows/language/index.ts +20 -0
- package/src/workflows/language/references.ts +55 -0
- package/src/workflows/language/schema.ts +134 -0
- package/src/workflows/language/strict-yaml.ts +131 -0
- package/src/workflows/manifest.ts +24 -0
- package/src/workflows/module.ts +12 -0
- package/src/workflows/runtime/coordinator.ts +115 -0
- package/src/workflows/runtime/executor.ts +1003 -0
- package/src/workflows/runtime/index.ts +60 -0
- package/src/workflows/runtime/instant.ts +8 -0
- package/src/workflows/runtime/ports.ts +213 -0
- package/src/workflows/runtime/schedule.ts +203 -0
- package/src/workflows/runtime/trigger.ts +37 -0
- package/src/workflows/runtime/wake.ts +13 -0
- package/src/workflows/store/actions.ts +438 -0
- package/src/workflows/store/budget.ts +145 -0
- package/src/workflows/store/definitions.ts +345 -0
- package/src/workflows/store/events.ts +383 -0
- package/src/workflows/store/index.ts +107 -0
- package/src/workflows/store/observability.ts +594 -0
- package/src/workflows/store/runs.ts +975 -0
- package/src/workflows/store/transaction.ts +23 -0
- package/src/workflows/store/worker.ts +439 -0
- package/src/workflows/testing/conformance.ts +72 -0
- package/src/workflows/testing/index.ts +11 -0
- package/src/workflows/testing/process-fixtures.ts +275 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { sql } from "bun";
|
|
2
|
+
import type { MutationResult, UserProfile } from "../../../contracts/shared";
|
|
3
|
+
import { isAccountCategoryAllowed } from "../../account-category-policy";
|
|
4
|
+
import { writeDeletedAccountAudit } from "../../account-lifecycle/audit";
|
|
5
|
+
import { resolveStoredAdminState } from "../../accounts/model";
|
|
6
|
+
import { writeLocalAccount } from "../../accounts/posix";
|
|
7
|
+
import type { AuditActor } from "../../audit";
|
|
8
|
+
import { generateUniqueAbbreviation } from "../../ipa/users";
|
|
9
|
+
import { isUniqueViolation } from "../../postgres";
|
|
10
|
+
import { session } from "../../session";
|
|
11
|
+
import * as settings from "../../settings";
|
|
12
|
+
|
|
13
|
+
type DbRow = Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
export type LocalUserCreateData = {
|
|
16
|
+
email: string;
|
|
17
|
+
givenname?: string;
|
|
18
|
+
sn?: string;
|
|
19
|
+
displayName?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const createLocalUid = async (): Promise<string> => {
|
|
23
|
+
const abbrLen = await settings.get<number>("user.abbr_length");
|
|
24
|
+
return generateUniqueAbbreviation(abbrLen);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const create = async (params: {
|
|
28
|
+
data: LocalUserCreateData;
|
|
29
|
+
profile: UserProfile;
|
|
30
|
+
accountExpires: Date | null;
|
|
31
|
+
admin?: boolean;
|
|
32
|
+
actor?: AuditActor;
|
|
33
|
+
}): Promise<MutationResult<{ id: string }>> => {
|
|
34
|
+
const uid = await createLocalUid();
|
|
35
|
+
const admin = resolveStoredAdminState({
|
|
36
|
+
provider: "local",
|
|
37
|
+
profile: params.profile,
|
|
38
|
+
requestedAdmin: params.admin,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
return await writeLocalAccount(async (tx) => {
|
|
43
|
+
if (!(await isAccountCategoryAllowed({ provider: "local", profile: params.profile }, tx)))
|
|
44
|
+
return { ok: false, error: "This account category is disabled. Contact an administrator.", status: 403 };
|
|
45
|
+
const rows = await tx<{ id: string }[]>`
|
|
46
|
+
INSERT INTO auth.users (
|
|
47
|
+
uid,
|
|
48
|
+
provider,
|
|
49
|
+
profile,
|
|
50
|
+
mail,
|
|
51
|
+
given_name,
|
|
52
|
+
sn,
|
|
53
|
+
display_name,
|
|
54
|
+
admin,
|
|
55
|
+
account_expires
|
|
56
|
+
)
|
|
57
|
+
VALUES (
|
|
58
|
+
${uid},
|
|
59
|
+
'local',
|
|
60
|
+
${params.profile},
|
|
61
|
+
${params.data.email},
|
|
62
|
+
${params.data.givenname ?? ""},
|
|
63
|
+
${params.data.sn ?? ""},
|
|
64
|
+
${params.data.displayName ?? ""},
|
|
65
|
+
${admin},
|
|
66
|
+
${params.accountExpires}
|
|
67
|
+
)
|
|
68
|
+
RETURNING id
|
|
69
|
+
`;
|
|
70
|
+
return { ok: true, data: { id: rows[0]!.id } };
|
|
71
|
+
}, params.actor);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Map Postgres unique violations to a typed 409 instead of leaking raw DB
|
|
74
|
+
// errors. Two possible collisions: generated uid (extremely rare, retry at
|
|
75
|
+
// next call) or email already used by another local account.
|
|
76
|
+
if (isUniqueViolation(error, "users_uid_key")) {
|
|
77
|
+
return { ok: false, error: "Generated UID collided. Please retry.", status: 409 };
|
|
78
|
+
}
|
|
79
|
+
if (isUniqueViolation(error, "idx_users_provider_mail")) {
|
|
80
|
+
return { ok: false, error: "A local account with this email already exists.", status: 409 };
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const createGuest = async (params: {
|
|
87
|
+
email: string;
|
|
88
|
+
givenname?: string;
|
|
89
|
+
sn?: string;
|
|
90
|
+
displayName?: string;
|
|
91
|
+
accountExpires?: Date | null;
|
|
92
|
+
}): Promise<MutationResult<{ id: string }>> => {
|
|
93
|
+
let accountExpires = params.accountExpires ?? null;
|
|
94
|
+
if (params.accountExpires === undefined) {
|
|
95
|
+
const configured = await settings.get<number | null>("user.account.local_guest_expires_days");
|
|
96
|
+
const days = typeof configured === "number" ? configured : 365;
|
|
97
|
+
accountExpires = days > 0 ? new Date(Date.now() + days * 24 * 60 * 60 * 1000) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return create({
|
|
101
|
+
data: {
|
|
102
|
+
email: params.email,
|
|
103
|
+
givenname: params.givenname,
|
|
104
|
+
sn: params.sn,
|
|
105
|
+
displayName: params.displayName,
|
|
106
|
+
},
|
|
107
|
+
profile: "guest",
|
|
108
|
+
accountExpires,
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const update = async (params: {
|
|
113
|
+
id: string;
|
|
114
|
+
data: {
|
|
115
|
+
givenname?: string;
|
|
116
|
+
sn?: string;
|
|
117
|
+
displayName?: string;
|
|
118
|
+
mail?: string;
|
|
119
|
+
};
|
|
120
|
+
}): Promise<MutationResult<void>> => {
|
|
121
|
+
const existingRows = await sql<DbRow[]>`SELECT id FROM auth.users WHERE id = ${params.id}::uuid AND provider = 'local'`;
|
|
122
|
+
if (existingRows.length === 0) {
|
|
123
|
+
return { ok: false, error: "Local user not found", status: 404 };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await sql`
|
|
127
|
+
UPDATE auth.users
|
|
128
|
+
SET given_name = CASE WHEN ${params.data.givenname !== undefined} THEN ${params.data.givenname ?? ""} ELSE given_name END,
|
|
129
|
+
sn = CASE WHEN ${params.data.sn !== undefined} THEN ${params.data.sn ?? ""} ELSE sn END,
|
|
130
|
+
display_name = CASE WHEN ${params.data.displayName !== undefined} THEN ${params.data.displayName ?? ""} ELSE display_name END,
|
|
131
|
+
mail = CASE WHEN ${params.data.mail !== undefined} THEN ${params.data.mail ?? null} ELSE mail END
|
|
132
|
+
WHERE id = ${params.id}::uuid
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
return { ok: true, data: undefined };
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const setProfile = async (params: {
|
|
139
|
+
id: string;
|
|
140
|
+
profile: UserProfile;
|
|
141
|
+
accountExpires: Date | null;
|
|
142
|
+
actor?: AuditActor;
|
|
143
|
+
}): Promise<MutationResult<void>> => {
|
|
144
|
+
const result = await writeLocalAccount(async (tx) => {
|
|
145
|
+
const rows = await tx<{ provider: string; profile: UserProfile; admin: boolean }[]>`
|
|
146
|
+
SELECT provider, profile, admin
|
|
147
|
+
FROM auth.users
|
|
148
|
+
WHERE id = ${params.id}::uuid
|
|
149
|
+
`;
|
|
150
|
+
if (rows.length === 0) return { ok: false, error: "User not found", status: 404 };
|
|
151
|
+
if (rows[0]!.provider !== "local") {
|
|
152
|
+
return { ok: false, error: "Only local accounts can change profile locally", status: 400 };
|
|
153
|
+
}
|
|
154
|
+
if (rows[0]!.profile !== params.profile && !(await isAccountCategoryAllowed({ provider: "local", profile: params.profile }, tx)))
|
|
155
|
+
return { ok: false, error: "This account category is disabled. Contact an administrator.", status: 403 };
|
|
156
|
+
|
|
157
|
+
const admin = resolveStoredAdminState({
|
|
158
|
+
provider: "local",
|
|
159
|
+
profile: params.profile,
|
|
160
|
+
currentAdmin: Boolean(rows[0]!.admin),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await tx`
|
|
164
|
+
UPDATE auth.users
|
|
165
|
+
SET provider = 'local',
|
|
166
|
+
profile = ${params.profile},
|
|
167
|
+
admin = ${admin},
|
|
168
|
+
account_expires = ${params.accountExpires}
|
|
169
|
+
WHERE id = ${params.id}::uuid
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
return { ok: true, data: { id: params.id, assignIdentity: rows[0]!.profile === "guest" && params.profile === "user" } };
|
|
173
|
+
}, params.actor);
|
|
174
|
+
return result.ok ? { ok: true, data: undefined } : result;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const setExpiry = async (params: {
|
|
178
|
+
id: string;
|
|
179
|
+
profile: UserProfile;
|
|
180
|
+
accountExpires: Date | null;
|
|
181
|
+
}): Promise<MutationResult<void>> => {
|
|
182
|
+
const rows = await sql<DbRow[]>`
|
|
183
|
+
SELECT provider
|
|
184
|
+
FROM auth.users
|
|
185
|
+
WHERE id = ${params.id}::uuid
|
|
186
|
+
`;
|
|
187
|
+
if (rows.length === 0) return { ok: false, error: "User not found", status: 404 };
|
|
188
|
+
if ((rows[0]!.provider as string) !== "local") {
|
|
189
|
+
return { ok: false, error: "Only local accounts support local expiry changes", status: 400 };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await sql`
|
|
193
|
+
UPDATE auth.users
|
|
194
|
+
SET account_expires = ${params.accountExpires}
|
|
195
|
+
WHERE id = ${params.id}::uuid
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
return { ok: true, data: undefined };
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const setAdmin = async (params: { id: string; admin: boolean }): Promise<MutationResult<void>> => {
|
|
202
|
+
const rows = await sql<DbRow[]>`
|
|
203
|
+
SELECT provider, profile, admin
|
|
204
|
+
FROM auth.users
|
|
205
|
+
WHERE id = ${params.id}::uuid
|
|
206
|
+
`;
|
|
207
|
+
if (rows.length === 0) return { ok: false, error: "User not found", status: 404 };
|
|
208
|
+
const row = rows[0]!;
|
|
209
|
+
if ((row.provider as string) !== "local") {
|
|
210
|
+
return { ok: false, error: "Only local full accounts can be granted admin access", status: 400 };
|
|
211
|
+
}
|
|
212
|
+
if ((row.profile as string) !== "user") {
|
|
213
|
+
return { ok: false, error: "Guest accounts cannot be granted admin access", status: 400 };
|
|
214
|
+
}
|
|
215
|
+
const currentAdmin = Boolean(row.admin);
|
|
216
|
+
if (currentAdmin === params.admin) {
|
|
217
|
+
return { ok: false, error: params.admin ? "Account is already an admin" : "Account is not an admin", status: 409 };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
await sql`
|
|
221
|
+
UPDATE auth.users
|
|
222
|
+
SET admin = ${params.admin}
|
|
223
|
+
WHERE id = ${params.id}::uuid
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
return { ok: true, data: undefined };
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const remove = async (params: { id: string; actor: { userId: string; uid: string } }): Promise<MutationResult<void>> => {
|
|
230
|
+
const rows = await sql<DbRow[]>`
|
|
231
|
+
SELECT uid, profile, mail, display_name
|
|
232
|
+
FROM auth.users
|
|
233
|
+
WHERE id = ${params.id}::uuid
|
|
234
|
+
AND provider = 'local'
|
|
235
|
+
`;
|
|
236
|
+
if (rows.length === 0) return { ok: false, error: "Local user not found", status: 404 };
|
|
237
|
+
|
|
238
|
+
const row = rows[0]!;
|
|
239
|
+
const uid = row.uid as string;
|
|
240
|
+
await sql.begin(async (tx) => {
|
|
241
|
+
await writeDeletedAccountAudit({
|
|
242
|
+
db: tx,
|
|
243
|
+
userId: params.id,
|
|
244
|
+
uid,
|
|
245
|
+
mail: (row.mail as string) ?? null,
|
|
246
|
+
displayName: (row.display_name as string) ?? null,
|
|
247
|
+
previousProvider: "local",
|
|
248
|
+
previousProfile: (row.profile as UserProfile | null) ?? "guest",
|
|
249
|
+
reason: "manual_delete",
|
|
250
|
+
meta: {
|
|
251
|
+
actorUserId: params.actor.userId,
|
|
252
|
+
actorUid: params.actor.uid,
|
|
253
|
+
deletedFromFreeIpa: false,
|
|
254
|
+
freeIpaUserAlreadyMissing: false,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
await tx`DELETE FROM auth.users WHERE id = ${params.id}::uuid`;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
await session.revokeAllForUser(params.id);
|
|
261
|
+
|
|
262
|
+
return { ok: true, data: undefined };
|
|
263
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SQL, sql } from "bun";
|
|
2
|
+
import { defaultRailPreferences, type RailPreferences, RailPreferencesSchema } from "../contracts/rail-preferences";
|
|
3
|
+
|
|
4
|
+
/** Core-owned data, read by the platform SSR seam without a process-wide cache. */
|
|
5
|
+
export const createRailPreferencesService = (db: SQL = sql) => ({
|
|
6
|
+
async get(userId: string): Promise<RailPreferences> {
|
|
7
|
+
const [row] = await db<{ revision: number; visibility: unknown; shortcuts: unknown }[]>`
|
|
8
|
+
SELECT revision, visibility, shortcuts FROM auth.rail_preferences WHERE user_id = ${userId}::uuid
|
|
9
|
+
`;
|
|
10
|
+
return row ? RailPreferencesSchema.parse(row) : defaultRailPreferences();
|
|
11
|
+
},
|
|
12
|
+
async save(userId: string, input: RailPreferences): Promise<RailPreferences | null> {
|
|
13
|
+
const value = RailPreferencesSchema.parse(input);
|
|
14
|
+
const [row] = await db<{ revision: number; visibility: unknown; shortcuts: unknown }[]>`
|
|
15
|
+
INSERT INTO auth.rail_preferences (user_id, revision, visibility, shortcuts)
|
|
16
|
+
SELECT ${userId}::uuid, 1, (${JSON.stringify(value.visibility)}::text)::jsonb, (${JSON.stringify(value.shortcuts)}::text)::jsonb
|
|
17
|
+
WHERE ${value.revision} = 0 OR EXISTS (SELECT 1 FROM auth.rail_preferences WHERE user_id = ${userId}::uuid)
|
|
18
|
+
ON CONFLICT (user_id) DO UPDATE SET
|
|
19
|
+
revision = auth.rail_preferences.revision + 1,
|
|
20
|
+
visibility = EXCLUDED.visibility, shortcuts = EXCLUDED.shortcuts
|
|
21
|
+
WHERE auth.rail_preferences.revision = ${value.revision}
|
|
22
|
+
RETURNING revision, visibility, shortcuts
|
|
23
|
+
`;
|
|
24
|
+
return row ? RailPreferencesSchema.parse(row) : null;
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
export const railPreferences = createRailPreferencesService();
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { Worker } from "@k2b/sync";
|
|
2
|
+
|
|
3
|
+
export type RuntimeTaskTracker = ReturnType<typeof createRuntimeTaskTracker>;
|
|
4
|
+
|
|
5
|
+
export type RuntimeTaskFailure = {
|
|
6
|
+
error: unknown;
|
|
7
|
+
failureCount: number;
|
|
8
|
+
retryInMs: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type RuntimeTaskSupervisorOptions = {
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
run(signal: AbortSignal): Promise<void>;
|
|
14
|
+
onError?(failure: RuntimeTaskFailure): void;
|
|
15
|
+
minRetryMs?: number;
|
|
16
|
+
maxRetryMs?: number;
|
|
17
|
+
resetAfterMs?: number;
|
|
18
|
+
jitter?: number;
|
|
19
|
+
name?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const waitForRetry = (delayMs: number, signal: AbortSignal): Promise<void> =>
|
|
23
|
+
new Promise((resolve) => {
|
|
24
|
+
if (signal.aborted) return resolve();
|
|
25
|
+
const finish = () => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
signal.removeEventListener("abort", finish);
|
|
28
|
+
resolve();
|
|
29
|
+
};
|
|
30
|
+
const timer = setTimeout(finish, delayMs);
|
|
31
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
32
|
+
if (signal.aborted) finish();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Restart a long-lived task after unexpected failure or completion. */
|
|
36
|
+
export const superviseRuntimeTask = async (options: RuntimeTaskSupervisorOptions): Promise<void> => {
|
|
37
|
+
const minRetryMs = options.minRetryMs ?? 250;
|
|
38
|
+
const maxRetryMs = options.maxRetryMs ?? 5_000;
|
|
39
|
+
const resetAfterMs = options.resetAfterMs ?? 30_000;
|
|
40
|
+
const jitter = options.jitter ?? 0.2;
|
|
41
|
+
if (![minRetryMs, maxRetryMs, resetAfterMs].every((value) => Number.isFinite(value) && value > 0)) {
|
|
42
|
+
throw new RangeError("Runtime task retry timings must be positive finite numbers");
|
|
43
|
+
}
|
|
44
|
+
if (minRetryMs > maxRetryMs) throw new RangeError("Runtime task minimum retry delay cannot exceed its maximum");
|
|
45
|
+
if (!Number.isFinite(jitter) || jitter < 0 || jitter > 1) throw new RangeError("Runtime task retry jitter must be between 0 and 1");
|
|
46
|
+
|
|
47
|
+
let failureCount = 0;
|
|
48
|
+
while (!options.signal.aborted) {
|
|
49
|
+
const startedAt = Date.now();
|
|
50
|
+
let error: unknown;
|
|
51
|
+
try {
|
|
52
|
+
await options.run(options.signal);
|
|
53
|
+
if (options.signal.aborted) return;
|
|
54
|
+
error = new Error(`${options.name ?? "Runtime task"} stopped unexpectedly`);
|
|
55
|
+
} catch (caught) {
|
|
56
|
+
if (options.signal.aborted) return;
|
|
57
|
+
error = caught;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
failureCount = Date.now() - startedAt >= resetAfterMs ? 1 : failureCount + 1;
|
|
61
|
+
const exponentialRetryMs = minRetryMs * 2 ** Math.min(failureCount - 1, 30);
|
|
62
|
+
const retryInMs = Math.min(maxRetryMs, Math.max(minRetryMs, Math.round(exponentialRetryMs * (1 + (Math.random() * 2 - 1) * jitter))));
|
|
63
|
+
try {
|
|
64
|
+
options.onError?.({ error, failureCount, retryInMs });
|
|
65
|
+
} catch {
|
|
66
|
+
// Reporting failures must not stop recovery.
|
|
67
|
+
}
|
|
68
|
+
await waitForRetry(retryInMs, options.signal);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Track process work outside Sync handlers, such as recovery scans and publishers. */
|
|
73
|
+
export const createRuntimeTaskTracker = () => {
|
|
74
|
+
const tasks = new Set<Promise<unknown>>();
|
|
75
|
+
let accepting = false;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
open: (): void => {
|
|
79
|
+
accepting = true;
|
|
80
|
+
},
|
|
81
|
+
close: (): void => {
|
|
82
|
+
accepting = false;
|
|
83
|
+
},
|
|
84
|
+
run: <T>(operation: () => Promise<T>): Promise<T> | null => {
|
|
85
|
+
if (!accepting) return null;
|
|
86
|
+
const task = Promise.resolve().then(operation);
|
|
87
|
+
tasks.add(task);
|
|
88
|
+
const remove = () => tasks.delete(task);
|
|
89
|
+
task.then(remove, remove);
|
|
90
|
+
return task;
|
|
91
|
+
},
|
|
92
|
+
drain: async (): Promise<void> => {
|
|
93
|
+
while (tasks.size > 0) await Promise.allSettled([...tasks]);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Stop accepting runtime tasks, stop the @k2b/sync workers from pulling new
|
|
100
|
+
* work, then drain accepted process tasks and workers together. Sync owns
|
|
101
|
+
* handler tracking and cancellation; do not wrap worker handlers in tracker.run.
|
|
102
|
+
* Only work outside those handlers uses the tracker and its separate deadline.
|
|
103
|
+
*/
|
|
104
|
+
export const stopRuntimeJobs = async (
|
|
105
|
+
tracker: Pick<RuntimeTaskTracker, "close" | "drain">,
|
|
106
|
+
workers: ReadonlyArray<Pick<Worker, "stop" | "drain">>,
|
|
107
|
+
options: { timeoutMs?: number } = {},
|
|
108
|
+
): Promise<void> => {
|
|
109
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
110
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new RangeError("Runtime drain timeout must be positive and finite");
|
|
111
|
+
tracker.close();
|
|
112
|
+
const errors: unknown[] = [];
|
|
113
|
+
for (const worker of workers) worker.stop();
|
|
114
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
115
|
+
const tracked = Promise.race([
|
|
116
|
+
tracker.drain(),
|
|
117
|
+
new Promise<never>((_, reject) => {
|
|
118
|
+
timer = setTimeout(() => reject(new Error(`Runtime tasks did not drain within ${timeoutMs}ms`)), timeoutMs);
|
|
119
|
+
}),
|
|
120
|
+
]);
|
|
121
|
+
try {
|
|
122
|
+
for (const result of await Promise.allSettled([tracked, ...workers.map((worker) => worker.drain({ timeoutMs }))])) {
|
|
123
|
+
if (result.status === "rejected") errors.push(result.reason);
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
}
|
|
128
|
+
if (errors.length === 1) throw errors[0];
|
|
129
|
+
if (errors.length > 1) throw new AggregateError(errors, "Multiple runtime jobs failed to stop");
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
type RuntimeLifecycleHooks = {
|
|
133
|
+
start(): Promise<void>;
|
|
134
|
+
stop(): Promise<void>;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const createRuntimeLifecycle = (hooks: RuntimeLifecycleHooks) => {
|
|
138
|
+
let state: "stopped" | "started" | "cleanup-required" = "stopped";
|
|
139
|
+
let transition = Promise.resolve();
|
|
140
|
+
|
|
141
|
+
const serialize = (operation: () => Promise<void>): Promise<void> => {
|
|
142
|
+
const result = transition.then(operation, operation);
|
|
143
|
+
transition = result.catch(() => undefined);
|
|
144
|
+
return result;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
start: (): Promise<void> =>
|
|
149
|
+
serialize(async () => {
|
|
150
|
+
if (state === "started") return;
|
|
151
|
+
if (state === "cleanup-required") {
|
|
152
|
+
await hooks.stop();
|
|
153
|
+
state = "stopped";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
state = "cleanup-required";
|
|
157
|
+
try {
|
|
158
|
+
await hooks.start();
|
|
159
|
+
state = "started";
|
|
160
|
+
} catch (startError) {
|
|
161
|
+
try {
|
|
162
|
+
await hooks.stop();
|
|
163
|
+
state = "stopped";
|
|
164
|
+
} catch (stopError) {
|
|
165
|
+
throw new AggregateError([startError, stopError], "Runtime startup and cleanup failed");
|
|
166
|
+
}
|
|
167
|
+
throw startError;
|
|
168
|
+
}
|
|
169
|
+
}),
|
|
170
|
+
stop: (): Promise<void> =>
|
|
171
|
+
serialize(async () => {
|
|
172
|
+
if (state === "stopped") return;
|
|
173
|
+
state = "cleanup-required";
|
|
174
|
+
await hooks.stop();
|
|
175
|
+
state = "stopped";
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const stopRuntimeResources = async (resources: ReadonlyArray<() => void | Promise<void>>): Promise<void> => {
|
|
181
|
+
const errors: unknown[] = [];
|
|
182
|
+
for (const stop of resources) {
|
|
183
|
+
try {
|
|
184
|
+
await stop();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
errors.push(error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (errors.length === 1) throw errors[0];
|
|
190
|
+
if (errors.length > 1) throw new AggregateError(errors, "Multiple runtime resources failed to stop");
|
|
191
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { decryptValue, encryptValue } from "./settings/crypto";
|
|
2
|
+
|
|
3
|
+
export const encryptSecret = async (value: unknown): Promise<string> => encryptValue(value);
|
|
4
|
+
|
|
5
|
+
export const decryptSecret = async <T = unknown>(value: string): Promise<T> => (await decryptValue(value)) as T;
|
|
6
|
+
|
|
7
|
+
export const secrets = {
|
|
8
|
+
encrypt: encryptSecret,
|
|
9
|
+
decrypt: decryptSecret,
|
|
10
|
+
};
|