@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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preload script for dev mode.
|
|
3
|
+
* Registers the SSR plugin (Solid.js JSX transform + island bundling)
|
|
4
|
+
* and builds CSS before any app code is imported.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the two consumer shapes of scripts/build.ts:
|
|
7
|
+
*
|
|
8
|
+
* Monorepo (this repo):
|
|
9
|
+
* APP_ID=<id> bun run --preload=packages/cloud/scripts/preload.ts ...
|
|
10
|
+
* # appDir defaults to packages/<APP_ID>, run from workspace root.
|
|
11
|
+
*
|
|
12
|
+
* Standalone (npm consumer, see cloud-template):
|
|
13
|
+
* APP_ID=<id> APP_DIR=. bun run --preload=node_modules/@k2b/cloud/scripts/preload.ts ...
|
|
14
|
+
* # appDir = APP_DIR (resolved against cwd), i.e. the directory holding src/.
|
|
15
|
+
*
|
|
16
|
+
* Uses bun-plugin-tailwind with root=cwd for stable path resolution. Each app
|
|
17
|
+
* stylesheet owns an explicit, app-local @source contract so the development
|
|
18
|
+
* build cannot leak classes from sibling apps.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, watch } from "node:fs";
|
|
21
|
+
import { cp, mkdir } from "node:fs/promises";
|
|
22
|
+
import { dirname, resolve } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import tailwind from "bun-plugin-tailwind";
|
|
25
|
+
import { writeAppFavicon } from "./app-favicon";
|
|
26
|
+
|
|
27
|
+
const appId = process.env.APP_ID ?? "core";
|
|
28
|
+
|
|
29
|
+
// `root` = wherever the process was started. In the monorepo this is the
|
|
30
|
+
// workspace root, which is what the paths below used to be resolved against.
|
|
31
|
+
const root = process.cwd();
|
|
32
|
+
|
|
33
|
+
// Framework dir — works whether this script sits in packages/cloud/scripts/
|
|
34
|
+
// (monorepo) or node_modules/@k2b/cloud/scripts/ (npm install).
|
|
35
|
+
const frameworkDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
36
|
+
|
|
37
|
+
// App dir — APP_DIR override for standalone consumers, defaults to monorepo
|
|
38
|
+
// convention. Resolved against cwd if relative.
|
|
39
|
+
const appDir = process.env.APP_DIR ? resolve(root, process.env.APP_DIR) : resolve(root, "packages", appId);
|
|
40
|
+
if (!existsSync(appDir)) throw new Error(`Unknown app dir: ${appDir} (set APP_DIR or check APP_ID)`);
|
|
41
|
+
|
|
42
|
+
const { app, plugin } = await import(resolve(appDir, "src/config"));
|
|
43
|
+
Bun.plugin(plugin());
|
|
44
|
+
|
|
45
|
+
// ── Build CSS ───────────────────────────────────────────────────────────────
|
|
46
|
+
const publicDir = resolve(root, "public");
|
|
47
|
+
await mkdir(publicDir, { recursive: true });
|
|
48
|
+
|
|
49
|
+
const globalCssEntry = resolve(root, "styles.css");
|
|
50
|
+
|
|
51
|
+
const buildGlobalCss = async () => {
|
|
52
|
+
// Use the same global entrypoint as the production extras build.
|
|
53
|
+
await Bun.build({
|
|
54
|
+
entrypoints: [globalCssEntry],
|
|
55
|
+
outdir: publicDir,
|
|
56
|
+
naming: "global.css",
|
|
57
|
+
plugins: [tailwind],
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const appCssPath = resolve(appDir, "src/styles/app.css");
|
|
62
|
+
|
|
63
|
+
const buildAppCss = async () => {
|
|
64
|
+
// `naming: "app.css"` is essential — without it, Bun.build with `root` set
|
|
65
|
+
// preserves the directory structure (packages/<id>/src/styles/app.css) inside
|
|
66
|
+
// outdir, so Layout's `<link href="/public/<id>/app.css">` 404s and only
|
|
67
|
+
// global.css's classes reach the browser. That's why responsive grid
|
|
68
|
+
// utilities went missing on dashboard.
|
|
69
|
+
await Bun.build({
|
|
70
|
+
entrypoints: [appCssPath],
|
|
71
|
+
outdir: resolve(publicDir, appId),
|
|
72
|
+
naming: "app.css",
|
|
73
|
+
root,
|
|
74
|
+
plugins: [tailwind],
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const watchDevCss = (label: string, paths: string[], build: () => Promise<void>) => {
|
|
79
|
+
if (process.env.NODE_ENV !== "development") return;
|
|
80
|
+
|
|
81
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
82
|
+
let running = false;
|
|
83
|
+
let queued = false;
|
|
84
|
+
|
|
85
|
+
const rebuild = () => {
|
|
86
|
+
if (running) {
|
|
87
|
+
queued = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
running = true;
|
|
92
|
+
void build()
|
|
93
|
+
.then(() => console.log(`[preload] rebuilt ${label}`))
|
|
94
|
+
.catch((error) => console.error(`[preload] failed to rebuild ${label}`, error))
|
|
95
|
+
.finally(() => {
|
|
96
|
+
running = false;
|
|
97
|
+
if (queued) {
|
|
98
|
+
queued = false;
|
|
99
|
+
rebuild();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const schedule = () => {
|
|
105
|
+
if (timer) clearTimeout(timer);
|
|
106
|
+
timer = setTimeout(rebuild, 75);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
for (const path of paths) {
|
|
110
|
+
if (!existsSync(path)) continue;
|
|
111
|
+
watch(path, { persistent: true }, schedule);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// global.css + branding are only served by core (Traefik routes them there).
|
|
116
|
+
// A standalone app consumes both from the prebuilt core container, so the
|
|
117
|
+
// workspace entrypoint is absent there and this block simply does not run.
|
|
118
|
+
if (appId === "core" && existsSync(globalCssEntry)) {
|
|
119
|
+
await buildGlobalCss();
|
|
120
|
+
|
|
121
|
+
// Default branding asset: copy the tracked logo.svg into the runtime
|
|
122
|
+
// public dir so serveBranding can fall back to it when no admin-uploaded
|
|
123
|
+
// logo (data URI) is configured. User uploads are stored as base64 data
|
|
124
|
+
// URIs in settings — no image processing (sharp etc.) needed.
|
|
125
|
+
await cp(resolve(frameworkDir, "public/logo.svg"), resolve(publicDir, "logo.svg"));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// katex.css is only needed by notebooks (served by core via Traefik /public/katex.css)
|
|
129
|
+
if (appId === "notebooks") {
|
|
130
|
+
try {
|
|
131
|
+
await cp(resolve(root, "node_modules/katex/dist/katex.min.css"), resolve(publicDir, "katex.css"));
|
|
132
|
+
} catch {
|
|
133
|
+
console.warn("[preload] katex.css not found, skipping");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Each app builds its own app.css
|
|
138
|
+
const appPublicDir = resolve(publicDir, appId);
|
|
139
|
+
await mkdir(appPublicDir, { recursive: true });
|
|
140
|
+
|
|
141
|
+
if (appId !== "core" && app) {
|
|
142
|
+
await writeAppFavicon({
|
|
143
|
+
publicDir,
|
|
144
|
+
appId,
|
|
145
|
+
icon: app.meta.icon,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await buildAppCss();
|
|
150
|
+
|
|
151
|
+
watchDevCss("app.css", [resolve(appCssPath, "..")], buildAppCss);
|
|
152
|
+
if (appId === "core" && existsSync(globalCssEntry)) {
|
|
153
|
+
watchDevCss("global.css", [globalCssEntry, resolve(frameworkDir, "src/styles")], buildGlobalCss);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Optional app-owned dev assets. Production builds already have
|
|
157
|
+
// scripts/build-extras.ts; this mirrors that hook for watch mode without
|
|
158
|
+
// forcing app-specific asset logic into the framework preload.
|
|
159
|
+
const devExtras = resolve(appDir, "scripts/dev-extras.ts");
|
|
160
|
+
if (existsSync(devExtras)) {
|
|
161
|
+
process.env.WORKSPACE_ROOT = root;
|
|
162
|
+
process.env.PUBLIC_DIR = publicDir;
|
|
163
|
+
await import(devExtras);
|
|
164
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated two-process recovery acceptance for the local NATS cluster.
|
|
3
|
+
*
|
|
4
|
+
* SYNC_RECOVERY_NAMESPACE=cloud-recovery-smoke-<unique> bun packages/cloud/scripts/sync-recovery-smoke.ts prepare
|
|
5
|
+
* Stop the application fleet across the printed nextTick boundary, keeping NATS up.
|
|
6
|
+
* Restart the fleet, then run the same command with `recover`.
|
|
7
|
+
* No provider or application-domain effects; successful recovery deletes only
|
|
8
|
+
* the exact test streams after checking their broker identity metadata.
|
|
9
|
+
*/
|
|
10
|
+
import { createSync, type SyncResourceSummary, type Worker } from "@k2b/sync";
|
|
11
|
+
import { connect, type NatsConnection } from "@nats-io/transport-node";
|
|
12
|
+
|
|
13
|
+
const APPLICATION = "cloud-recovery-smoke";
|
|
14
|
+
const JOB_ID = "restart-job";
|
|
15
|
+
const SCHEDULER_ID = "restart-scheduler";
|
|
16
|
+
const SCHEDULE_ID = "minute";
|
|
17
|
+
const WAIT_MS = 20_000;
|
|
18
|
+
const object = (value: unknown): Record<string, unknown> => {
|
|
19
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected a broker object response");
|
|
20
|
+
return Object.fromEntries(Object.entries(value));
|
|
21
|
+
};
|
|
22
|
+
function check(condition: unknown, message: string): asserts condition {
|
|
23
|
+
if (!condition) throw new Error(message);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const removeTestStreams = async (connection: NatsConnection, namespace: string, resources: SyncResourceSummary[]) => {
|
|
27
|
+
let deleted = 0;
|
|
28
|
+
for (const resource of resources) {
|
|
29
|
+
check(resource.namespace === namespace && resource.owner === APPLICATION, "Refusing to remove a foreign resource");
|
|
30
|
+
check(resource.id === JOB_ID || resource.id === SCHEDULER_ID, "Refusing to remove an unexpected resource");
|
|
31
|
+
for (const name of resource.natsNames) {
|
|
32
|
+
check(/^[A-Za-z0-9_-]+$/.test(name), "Invalid broker stream name");
|
|
33
|
+
const info = object((await connection.request(`$JS.API.STREAM.INFO.${name}`, new Uint8Array())).json<unknown>());
|
|
34
|
+
const metadata = object(object(info.config).metadata);
|
|
35
|
+
check(
|
|
36
|
+
metadata["sync.managed"] === "true" &&
|
|
37
|
+
metadata["sync.namespace"] === namespace &&
|
|
38
|
+
metadata["sync.owner"] === APPLICATION &&
|
|
39
|
+
metadata["sync.id"] === resource.id,
|
|
40
|
+
"Broker identity does not match the isolated recovery resource",
|
|
41
|
+
);
|
|
42
|
+
const removed = object((await connection.request(`$JS.API.STREAM.DELETE.${name}`, new Uint8Array())).json<unknown>());
|
|
43
|
+
check(removed.success === true, "Broker refused recovery stream cleanup");
|
|
44
|
+
deleted++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return deleted;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const main = async (): Promise<void> => {
|
|
51
|
+
const phase = Bun.argv[2];
|
|
52
|
+
check(phase === "prepare" || phase === "recover", "Pass prepare or recover");
|
|
53
|
+
const namespace = process.env.SYNC_RECOVERY_NAMESPACE?.trim() ?? "";
|
|
54
|
+
check(/^cloud-recovery-smoke-[A-Za-z0-9_-]{6,60}$/.test(namespace), "Set a unique SYNC_RECOVERY_NAMESPACE=cloud-recovery-smoke-<suffix>");
|
|
55
|
+
const servers = (process.env.NATS_SERVERS ?? "nats://127.0.0.1:4222")
|
|
56
|
+
.split(",")
|
|
57
|
+
.map((value) => value.trim())
|
|
58
|
+
.filter(Boolean);
|
|
59
|
+
const connection = await connect({ servers, name: `${APPLICATION}-${phase}`, ignoreClusterUpdates: true });
|
|
60
|
+
const sync = createSync({ connection, namespace, application: APPLICATION, defaults: { replicas: 3 } });
|
|
61
|
+
const job = sync.job<{ preparedAt: string }>({
|
|
62
|
+
id: JOB_ID,
|
|
63
|
+
delivery: { ackWaitMs: 30_000, maxAttempts: 3, backoffMs: [1_000, 2_000] },
|
|
64
|
+
});
|
|
65
|
+
const scheduler = sync.scheduler({ id: SCHEDULER_ID, delivery: { maxAttempts: 3, backoffMs: [1_000, 2_000] } });
|
|
66
|
+
const workers: Worker[] = [];
|
|
67
|
+
let drained = false;
|
|
68
|
+
try {
|
|
69
|
+
await sync.ready();
|
|
70
|
+
if (phase === "prepare") {
|
|
71
|
+
check((await scheduler.get({ id: SCHEDULE_ID })) === null, "Namespace already prepared; use a new unique namespace");
|
|
72
|
+
const preparedAt = new Date().toISOString();
|
|
73
|
+
await scheduler.create({
|
|
74
|
+
id: SCHEDULE_ID,
|
|
75
|
+
cron: "* * * * *",
|
|
76
|
+
timezone: "UTC",
|
|
77
|
+
misfire: "latest",
|
|
78
|
+
meta: { preparedAt },
|
|
79
|
+
process: async () => {
|
|
80
|
+
throw new Error("Prepare phase must not start schedule workers");
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const receipt = await job.submit({ key: "pending-before-stop", input: { preparedAt }, coalesce: true });
|
|
84
|
+
const schedule = await scheduler.get({ id: SCHEDULE_ID });
|
|
85
|
+
check(schedule?.nextRunAt, "Broker did not persist the recovery schedule");
|
|
86
|
+
console.log(
|
|
87
|
+
JSON.stringify({
|
|
88
|
+
phase,
|
|
89
|
+
namespace,
|
|
90
|
+
preparedAt,
|
|
91
|
+
nextTick: schedule.nextRunAt.toISOString(),
|
|
92
|
+
jobId: receipt.jobId,
|
|
93
|
+
workersStarted: 0,
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const recoveredAt = Date.now();
|
|
100
|
+
const existing = await scheduler.get({ id: SCHEDULE_ID });
|
|
101
|
+
check(existing, "Prepared schedule missing; recovery cannot prove persistence");
|
|
102
|
+
const preparedAt = existing.meta?.preparedAt;
|
|
103
|
+
check(typeof preparedAt === "string" && Number.isFinite(Date.parse(preparedAt)), "Prepared schedule metadata missing");
|
|
104
|
+
check(existing.runNumber === 0, "Prepared schedule was already processed");
|
|
105
|
+
const firstTick = Math.floor(Date.parse(preparedAt) / 60_000) * 60_000 + 60_000;
|
|
106
|
+
check(recoveredAt > firstTick, "No missed minute boundary yet; leave NATS running until after nextTick");
|
|
107
|
+
const jobDone = Promise.withResolvers<void>();
|
|
108
|
+
const tickDone = Promise.withResolvers<void>();
|
|
109
|
+
let jobs = 0;
|
|
110
|
+
let ticks = 0;
|
|
111
|
+
let recoveredSlot: string | undefined;
|
|
112
|
+
const registration = await scheduler.create({
|
|
113
|
+
id: SCHEDULE_ID,
|
|
114
|
+
cron: "* * * * *",
|
|
115
|
+
timezone: "UTC",
|
|
116
|
+
misfire: "latest",
|
|
117
|
+
meta: { preparedAt },
|
|
118
|
+
process: async (context) => {
|
|
119
|
+
check(context.trigger === "schedule", "Expected a broker cron tick, not a manual run");
|
|
120
|
+
check(
|
|
121
|
+
context.slot.getTime() >= firstTick && context.slot.getTime() < recoveredAt,
|
|
122
|
+
"Cron tick was not retained from the offline interval",
|
|
123
|
+
);
|
|
124
|
+
recoveredSlot = context.slot.toISOString();
|
|
125
|
+
ticks++;
|
|
126
|
+
tickDone.resolve();
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
check(!registration.created && !registration.updated, "Re-registration unexpectedly changed the persisted schedule");
|
|
130
|
+
workers.push(
|
|
131
|
+
await job.process({ concurrency: 1 }, async (context) => {
|
|
132
|
+
check(context.input.preparedAt === preparedAt && context.key === "pending-before-stop", "Unexpected recovery job payload");
|
|
133
|
+
jobs++;
|
|
134
|
+
jobDone.resolve();
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
workers.push(await scheduler.process({ concurrency: 1 }));
|
|
138
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
139
|
+
try {
|
|
140
|
+
await Promise.race([
|
|
141
|
+
Promise.all([jobDone.promise, tickDone.promise]),
|
|
142
|
+
new Promise<never>((_, reject) => {
|
|
143
|
+
timeout = setTimeout(() => reject(new Error("Retained job or cron tick was not recovered within 20 seconds")), WAIT_MS);
|
|
144
|
+
}),
|
|
145
|
+
]);
|
|
146
|
+
} finally {
|
|
147
|
+
if (timeout) clearTimeout(timeout);
|
|
148
|
+
}
|
|
149
|
+
for (const worker of workers) worker.stop();
|
|
150
|
+
await Promise.all(workers.map((worker) => worker.drain()));
|
|
151
|
+
check(jobs === 1 && ticks === 1, "Unexpected duplicate work during isolated recovery");
|
|
152
|
+
const finalSchedule = await scheduler.get({ id: SCHEDULE_ID });
|
|
153
|
+
check(finalSchedule?.lastCompletedAt, "Recovered cron tick did not settle successfully");
|
|
154
|
+
const resources = await sync.resources();
|
|
155
|
+
check(
|
|
156
|
+
resources.every((resource) => resource.state === "ready"),
|
|
157
|
+
"Recovery resources are not ready",
|
|
158
|
+
);
|
|
159
|
+
check(
|
|
160
|
+
resources.every((resource) => !resource.detail?.deadLetters),
|
|
161
|
+
"Recovery created dead letters",
|
|
162
|
+
);
|
|
163
|
+
const recoveredJob = resources.find((resource) => resource.kind === "job" && resource.id === JOB_ID);
|
|
164
|
+
check(recoveredJob?.detail?.messages === 0, "Recovery job remains in the broker stream after drain");
|
|
165
|
+
const consumers = object(recoveredJob.detail.consumers);
|
|
166
|
+
check(Object.keys(consumers).length > 0, "Recovery job consumer diagnostics are missing");
|
|
167
|
+
for (const state of Object.values(consumers)) {
|
|
168
|
+
const consumer = object(state);
|
|
169
|
+
check(consumer.pending === 0 && consumer.ackPending === 0, "Recovery job has pending or unacknowledged broker deliveries");
|
|
170
|
+
}
|
|
171
|
+
await scheduler.delete({ id: SCHEDULE_ID });
|
|
172
|
+
await sync.drain();
|
|
173
|
+
drained = true;
|
|
174
|
+
const deletedStreams = await removeTestStreams(connection, namespace, resources);
|
|
175
|
+
console.log(
|
|
176
|
+
JSON.stringify({
|
|
177
|
+
phase,
|
|
178
|
+
namespace,
|
|
179
|
+
preparedAt,
|
|
180
|
+
recoveredAt: new Date(recoveredAt).toISOString(),
|
|
181
|
+
recoveredSlot,
|
|
182
|
+
jobs,
|
|
183
|
+
ticks,
|
|
184
|
+
pendingJobMessages: recoveredJob.detail.messages,
|
|
185
|
+
unacknowledgedJobMessages: 0,
|
|
186
|
+
deletedStreams,
|
|
187
|
+
outcome: "passed",
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
} finally {
|
|
191
|
+
for (const worker of workers) worker.stop();
|
|
192
|
+
try {
|
|
193
|
+
if (!drained) await sync.drain();
|
|
194
|
+
} finally {
|
|
195
|
+
await connection.drain();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (import.meta.main) await main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AppMeta, AppPresentationCatalog, AppPresentationTranslation } from "../contracts/app";
|
|
2
|
+
import { canonicalLocale } from "../shared/locale";
|
|
3
|
+
|
|
4
|
+
export const APP_PRESENTATION_MAX_BYTES = 64 * 1024;
|
|
5
|
+
const MAX_SHORT_TEXT = 200;
|
|
6
|
+
const MAX_DESCRIPTION = 2_000;
|
|
7
|
+
|
|
8
|
+
const text = (value: string, label: string, max = MAX_SHORT_TEXT): string => {
|
|
9
|
+
const normalized = value.trim();
|
|
10
|
+
if (!normalized || normalized.length > max) throw new Error(`${label} must contain 1 to ${max} characters`);
|
|
11
|
+
return normalized;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const labels = (values: Readonly<Record<string, string>> | undefined, allowed: Set<string>, label: string) => {
|
|
15
|
+
if (!values) return undefined;
|
|
16
|
+
const normalized: Record<string, string> = {};
|
|
17
|
+
for (const [key, value] of Object.entries(values)) {
|
|
18
|
+
if (!allowed.has(key)) throw new Error(`${label} contains unknown key ${JSON.stringify(key)}`);
|
|
19
|
+
normalized[key] = text(value, `${label}.${key}`);
|
|
20
|
+
}
|
|
21
|
+
return normalized;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Validate and canonicalize one static app-presentation catalog before it enters the registry. */
|
|
25
|
+
export const compileAppPresentation = (app: AppMeta, catalog: AppPresentationCatalog | undefined): AppPresentationCatalog | undefined => {
|
|
26
|
+
if (!catalog) return undefined;
|
|
27
|
+
const baseLocale = canonicalLocale(catalog.baseLocale);
|
|
28
|
+
if (!baseLocale) throw new Error("App presentation baseLocale must be a valid BCP 47 locale");
|
|
29
|
+
|
|
30
|
+
const groupIds = new Set<string>();
|
|
31
|
+
for (const group of app.adminNav ?? []) {
|
|
32
|
+
if (!group.id) continue;
|
|
33
|
+
if (groupIds.has(group.id)) throw new Error(`App presentation admin group id ${JSON.stringify(group.id)} is duplicated`);
|
|
34
|
+
groupIds.add(group.id);
|
|
35
|
+
}
|
|
36
|
+
const adminHrefs = new Set((app.adminNav ?? []).flatMap((group) => group.links.map((link) => link.href)));
|
|
37
|
+
const legalHrefs = new Set((app.legalLinks ?? []).map((link) => link.href));
|
|
38
|
+
const translations: Record<string, AppPresentationTranslation> = {};
|
|
39
|
+
|
|
40
|
+
for (const [locale, translation] of Object.entries(catalog.translations)) {
|
|
41
|
+
const canonical = canonicalLocale(locale);
|
|
42
|
+
if (!canonical) throw new Error(`App presentation locale ${JSON.stringify(locale)} is not a valid BCP 47 locale`);
|
|
43
|
+
if (canonical === baseLocale) throw new Error(`App presentation translations must not repeat base locale ${baseLocale}`);
|
|
44
|
+
if (translations[canonical]) throw new Error(`App presentation locale ${canonical} is duplicated after canonicalization`);
|
|
45
|
+
translations[canonical] = {
|
|
46
|
+
...(translation.name !== undefined ? { name: text(translation.name, `${canonical}.name`) } : {}),
|
|
47
|
+
...(translation.description !== undefined
|
|
48
|
+
? { description: text(translation.description, `${canonical}.description`, MAX_DESCRIPTION) }
|
|
49
|
+
: {}),
|
|
50
|
+
...(translation.adminGroups ? { adminGroups: labels(translation.adminGroups, groupIds, `${canonical}.adminGroups`) } : {}),
|
|
51
|
+
...(translation.adminLinks ? { adminLinks: labels(translation.adminLinks, adminHrefs, `${canonical}.adminLinks`) } : {}),
|
|
52
|
+
...(translation.legalLinks ? { legalLinks: labels(translation.legalLinks, legalHrefs, `${canonical}.legalLinks`) } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const compiled = { baseLocale, translations } satisfies AppPresentationCatalog;
|
|
57
|
+
if (new TextEncoder().encode(JSON.stringify(compiled)).byteLength > APP_PRESENTATION_MAX_BYTES) {
|
|
58
|
+
throw new Error(`App presentation catalog exceeds ${APP_PRESENTATION_MAX_BYTES} bytes`);
|
|
59
|
+
}
|
|
60
|
+
return compiled;
|
|
61
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type AppWorkspaceLayoutState, parseAppWorkspaceLayoutState } from "@k2b/ui";
|
|
2
|
+
|
|
3
|
+
const safeAppId = (appId: string): string => appId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
4
|
+
|
|
5
|
+
export const appWorkspaceCookieName = (appId: string): string => `cloud_workspace_${safeAppId(appId)}`;
|
|
6
|
+
|
|
7
|
+
export const readAppWorkspaceLayoutCookie = (
|
|
8
|
+
cookieHeader: string | null | undefined,
|
|
9
|
+
appId: string | null | undefined,
|
|
10
|
+
): AppWorkspaceLayoutState | null => {
|
|
11
|
+
if (!cookieHeader || !appId) return null;
|
|
12
|
+
const name = appWorkspaceCookieName(appId);
|
|
13
|
+
const encoded = cookieHeader
|
|
14
|
+
.split(";")
|
|
15
|
+
.map((part) => part.trim())
|
|
16
|
+
.find((part) => part.startsWith(`${name}=`))
|
|
17
|
+
?.slice(name.length + 1);
|
|
18
|
+
return parseAppWorkspaceLayoutState(encoded);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const resolveAppWorkspaceLayoutForSidebar = (
|
|
22
|
+
state: AppWorkspaceLayoutState | null,
|
|
23
|
+
collapsible: boolean | undefined,
|
|
24
|
+
): AppWorkspaceLayoutState | null => (collapsible === false && state?.sidebarCollapsed ? { ...state, sidebarCollapsed: false } : state);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type BoundedJsonResult = { ok: true; data: unknown } | { ok: false; reason: "too_large" | "invalid_json" };
|
|
2
|
+
|
|
3
|
+
/** Reads JSON without allowing a missing or forged Content-Length to bypass the byte cap. */
|
|
4
|
+
export const readBoundedJson = async (message: Request | Response, maxBytes: number): Promise<BoundedJsonResult> => {
|
|
5
|
+
const declared = message.headers.get("content-length");
|
|
6
|
+
if (declared !== null) {
|
|
7
|
+
const bytes = Number(declared);
|
|
8
|
+
if (Number.isFinite(bytes) && bytes > maxBytes) {
|
|
9
|
+
await message.body?.cancel().catch(() => undefined);
|
|
10
|
+
return { ok: false, reason: "too_large" };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const reader = message.body?.getReader();
|
|
15
|
+
if (!reader) return { ok: false, reason: "invalid_json" };
|
|
16
|
+
|
|
17
|
+
const chunks: Uint8Array[] = [];
|
|
18
|
+
let total = 0;
|
|
19
|
+
try {
|
|
20
|
+
while (true) {
|
|
21
|
+
const next = await reader.read();
|
|
22
|
+
if (next.done) break;
|
|
23
|
+
total += next.value.byteLength;
|
|
24
|
+
if (total > maxBytes) {
|
|
25
|
+
await reader.cancel().catch(() => undefined);
|
|
26
|
+
return { ok: false, reason: "too_large" };
|
|
27
|
+
}
|
|
28
|
+
chunks.push(next.value);
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
await reader.cancel().catch(() => undefined);
|
|
32
|
+
return { ok: false, reason: "invalid_json" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const bytes = new Uint8Array(total);
|
|
36
|
+
let offset = 0;
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
bytes.set(chunk, offset);
|
|
39
|
+
offset += chunk.byteLength;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
return { ok: true, data: JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)) };
|
|
44
|
+
} catch {
|
|
45
|
+
return { ok: false, reason: "invalid_json" };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AppRuntimeMetadata } from "../contracts/registry";
|
|
2
|
+
|
|
3
|
+
declare const __CLOUD_RELEASE__: string;
|
|
4
|
+
declare const __CLOUD_SYNC_VERSION__: string;
|
|
5
|
+
|
|
6
|
+
export const appRuntimeMetadata: AppRuntimeMetadata = {
|
|
7
|
+
release: typeof __CLOUD_RELEASE__ === "string" ? __CLOUD_RELEASE__ : "development",
|
|
8
|
+
syncVersion: typeof __CLOUD_SYNC_VERSION__ === "string" ? __CLOUD_SYNC_VERSION__ : "unknown",
|
|
9
|
+
};
|