@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,486 @@
|
|
|
1
|
+
import { mutation } from "@k2b/stdlib/solid";
|
|
2
|
+
import { Button, Combobox, type ComboboxOption, IconButton, Placeholder, prompts, SelectChip, Tooltip, useLocale } from "@k2b/ui";
|
|
3
|
+
import { createSignal, For, Show } from "solid-js";
|
|
4
|
+
import { CloudAvatar } from "../account/Avatar";
|
|
5
|
+
import type { AccessEntry, PermissionLevel, Principal } from "../contracts/shared";
|
|
6
|
+
import { accessMessages } from "./messages";
|
|
7
|
+
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
// Public API
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/** The three grantable permission levels — `"none"` exists in the
|
|
13
|
+
* contract for resolution semantics but is never directly granted. */
|
|
14
|
+
export type GrantableLevel = Exclude<PermissionLevel, "none">;
|
|
15
|
+
|
|
16
|
+
/** Either a bare level (uses the default View / Edit / Manage label and
|
|
17
|
+
* icon) or an object with per-context overrides. */
|
|
18
|
+
export type AllowedLevel = GrantableLevel | { level: GrantableLevel; label?: string; icon?: string };
|
|
19
|
+
|
|
20
|
+
type PermissionEditorProps = {
|
|
21
|
+
/** Initial access entries — caller stays the source of truth for
|
|
22
|
+
* what's stored on the resource; the editor updates its local copy
|
|
23
|
+
* after successful mutations. */
|
|
24
|
+
initialEntries: AccessEntry[];
|
|
25
|
+
|
|
26
|
+
/** Whether the current user can edit permissions. When `false`, the
|
|
27
|
+
* editor renders the entries read-only — no row dropdowns, no
|
|
28
|
+
* delete buttons, no add form. */
|
|
29
|
+
canEdit?: boolean;
|
|
30
|
+
|
|
31
|
+
/** Grant access. The caller closes over the resource id. The optional
|
|
32
|
+
* display metadata lets deferred form drafts retain the selected name. */
|
|
33
|
+
grantAccess: (principal: Principal, permission: GrantableLevel, display?: { displayName: string }) => Promise<AccessEntry>;
|
|
34
|
+
|
|
35
|
+
/** Update an existing entry's permission level. */
|
|
36
|
+
updateAccess: (accessId: string, permission: GrantableLevel) => Promise<void>;
|
|
37
|
+
|
|
38
|
+
/** Revoke an existing entry. The last entry IS deletable — for
|
|
39
|
+
* hierarchical resources the parent ACL still applies. */
|
|
40
|
+
revokeAccess: (accessId: string) => Promise<void>;
|
|
41
|
+
|
|
42
|
+
/** Allow granting `public` access from this editor. */
|
|
43
|
+
allowPublic?: boolean;
|
|
44
|
+
|
|
45
|
+
/** Allow granting access to every authenticated user. Defaults to
|
|
46
|
+
* true; resources with an explicit principal allowlist can disable it. */
|
|
47
|
+
allowAuthenticated?: boolean;
|
|
48
|
+
|
|
49
|
+
/** Allow granting service accounts. Off by default so ordinary
|
|
50
|
+
* permission pickers stay user/group focused. */
|
|
51
|
+
allowServiceAccounts?: boolean;
|
|
52
|
+
|
|
53
|
+
/** Which levels the UI offers — and what they're called. Bare strings
|
|
54
|
+
* use the default labels (View / Edit / Manage). Objects override
|
|
55
|
+
* label and/or icon for per-context vocabulary (e.g. forms call
|
|
56
|
+
* write "Use", views call read "View"). When undefined, all three
|
|
57
|
+
* are offered with default labels. New entries are granted
|
|
58
|
+
* `allowedLevels[0]` on pick — the user upgrades via the row pill
|
|
59
|
+
* afterwards. */
|
|
60
|
+
allowedLevels?: AllowedLevel[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
64
|
+
// Defaults & helpers
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
const defaultLabels = (t: ReturnType<typeof accessMessages.resolve>["t"]): Record<PermissionLevel, { label: string; icon: string }> => ({
|
|
68
|
+
read: { label: t.view, icon: "ti-eye" },
|
|
69
|
+
write: { label: t.edit, icon: "ti-pencil" },
|
|
70
|
+
admin: { label: t.manage, icon: "ti-shield" },
|
|
71
|
+
// Defensive — never granted by this editor, but renders correctly if
|
|
72
|
+
// a legacy entry has permission === "none".
|
|
73
|
+
none: { label: t.noAccess, icon: "ti-ban" },
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
type ResolvedLevel = {
|
|
77
|
+
level: GrantableLevel;
|
|
78
|
+
label: string;
|
|
79
|
+
icon: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Resolve the AllowedLevel union into a flat shape the renderer can
|
|
83
|
+
* loop over. Falls back to the default View / Edit / Manage list when
|
|
84
|
+
* no override is given. */
|
|
85
|
+
const resolveAllowedLevels = (allowed: AllowedLevel[] | undefined, t: ReturnType<typeof accessMessages.resolve>["t"]): ResolvedLevel[] => {
|
|
86
|
+
const defaults = defaultLabels(t);
|
|
87
|
+
const list = allowed && allowed.length > 0 ? allowed : (["read", "write", "admin"] as GrantableLevel[]);
|
|
88
|
+
return list.map((entry) => {
|
|
89
|
+
const level = typeof entry === "string" ? entry : entry.level;
|
|
90
|
+
const override = typeof entry === "string" ? null : entry;
|
|
91
|
+
const def = defaults[level];
|
|
92
|
+
return {
|
|
93
|
+
level,
|
|
94
|
+
label: override?.label ?? def.label,
|
|
95
|
+
icon: override?.icon ?? def.icon,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** Resolve a stored entry's permission to a renderable {label,icon},
|
|
101
|
+
* preferring the caller's allowedLevels override and falling back to
|
|
102
|
+
* the platform defaults. Tolerates "none" / unknown legacy values. */
|
|
103
|
+
const resolveEntryDisplay = (
|
|
104
|
+
permission: PermissionLevel,
|
|
105
|
+
allowed: ResolvedLevel[],
|
|
106
|
+
t: ReturnType<typeof accessMessages.resolve>["t"],
|
|
107
|
+
): { label: string; icon: string } => {
|
|
108
|
+
const fromAllowed = allowed.find((a) => a.level === permission);
|
|
109
|
+
if (fromAllowed) return fromAllowed;
|
|
110
|
+
const defaults = defaultLabels(t);
|
|
111
|
+
return defaults[permission] ?? defaults.none;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const getEntryDisplayName = (entry: AccessEntry, t: ReturnType<typeof accessMessages.resolve>["t"]): string => {
|
|
115
|
+
if (entry.displayName) return entry.displayName;
|
|
116
|
+
if (entry.principal.type === "authenticated") return t.allUsers;
|
|
117
|
+
if (entry.principal.type === "public") return t.public;
|
|
118
|
+
if (entry.principal.type === "user") return entry.principal.userId;
|
|
119
|
+
if (entry.principal.type === "service_account") return entry.principal.serviceAccountId;
|
|
120
|
+
return entry.principal.groupId;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const getPrincipalIcon = (principal: Principal): string => {
|
|
124
|
+
switch (principal.type) {
|
|
125
|
+
case "user":
|
|
126
|
+
return "ti-user";
|
|
127
|
+
case "group":
|
|
128
|
+
return "ti-users-group";
|
|
129
|
+
case "service_account":
|
|
130
|
+
return "ti-key";
|
|
131
|
+
case "authenticated":
|
|
132
|
+
return "ti-lock-open-2";
|
|
133
|
+
case "public":
|
|
134
|
+
return "ti-world";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Backend `/api/accounts/entities` shape (the subset we consume).
|
|
139
|
+
type ApiEntity =
|
|
140
|
+
| { kind: "user"; user: { id: string; uid: string; displayName: string; mail: string | null } }
|
|
141
|
+
| { kind: "group"; group: { id: string; name: string; description: string | null } }
|
|
142
|
+
| {
|
|
143
|
+
kind: "service_account";
|
|
144
|
+
serviceAccount: {
|
|
145
|
+
id: string;
|
|
146
|
+
name: string;
|
|
147
|
+
kind: "user_delegated" | "resource_bound";
|
|
148
|
+
appId: string | null;
|
|
149
|
+
resourceType: string | null;
|
|
150
|
+
resourceId: string | null;
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
155
|
+
// PermissionEditor
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
export default function PermissionEditor(props: PermissionEditorProps) {
|
|
159
|
+
const locale = useLocale();
|
|
160
|
+
const t = () => accessMessages.resolve([locale()]).t;
|
|
161
|
+
const [entries, setEntries] = createSignal<AccessEntry[]>([...props.initialEntries]);
|
|
162
|
+
const canEdit = () => props.canEdit !== false;
|
|
163
|
+
const allowPublic = () => props.allowPublic === true;
|
|
164
|
+
const allowAuthenticated = () => props.allowAuthenticated !== false;
|
|
165
|
+
const allowed = () => resolveAllowedLevels(props.allowedLevels, t());
|
|
166
|
+
const isSinglePicker = () => allowed().length === 1;
|
|
167
|
+
|
|
168
|
+
// Defensive dev-warning: an empty allowedLevels array makes the editor
|
|
169
|
+
// unable to grant anything.
|
|
170
|
+
if (props.allowedLevels && props.allowedLevels.length === 0) {
|
|
171
|
+
if (typeof console !== "undefined") {
|
|
172
|
+
console.warn(
|
|
173
|
+
"[PermissionEditor] `allowedLevels=[]` — the editor cannot grant any permission. Pass at least one level or omit the prop for the default View / Edit / Manage set.",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const existingUserIds = () =>
|
|
179
|
+
entries()
|
|
180
|
+
.filter((e) => e.principal.type === "user")
|
|
181
|
+
.map((e) => (e.principal as { type: "user"; userId: string }).userId);
|
|
182
|
+
const existingGroupIds = () =>
|
|
183
|
+
entries()
|
|
184
|
+
.filter((e) => e.principal.type === "group")
|
|
185
|
+
.map((e) => (e.principal as { type: "group"; groupId: string }).groupId);
|
|
186
|
+
const existingServiceAccountIds = () =>
|
|
187
|
+
entries()
|
|
188
|
+
.filter((e) => e.principal.type === "service_account")
|
|
189
|
+
.map((e) => (e.principal as { type: "service_account"; serviceAccountId: string }).serviceAccountId);
|
|
190
|
+
const hasAuthenticatedEntry = () => entries().some((entry) => entry.principal.type === "authenticated");
|
|
191
|
+
const hasPublicEntry = () => entries().some((entry) => entry.principal.type === "public");
|
|
192
|
+
|
|
193
|
+
const grantMut = mutation.create({
|
|
194
|
+
mutation: async (data: { principal: Principal; permission: GrantableLevel; display: { displayName: string } }) =>
|
|
195
|
+
props.grantAccess(data.principal, data.permission, data.display),
|
|
196
|
+
onSuccess: (newEntry) => {
|
|
197
|
+
setEntries([...entries(), newEntry as AccessEntry]);
|
|
198
|
+
},
|
|
199
|
+
onError: (err) => prompts.error(err.message),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const updateMut = mutation.create<{ accessId: string; permission: GrantableLevel }, { accessId: string; permission: GrantableLevel }>({
|
|
203
|
+
mutation: async (data) => {
|
|
204
|
+
await props.updateAccess(data.accessId, data.permission);
|
|
205
|
+
return data;
|
|
206
|
+
},
|
|
207
|
+
onSuccess: (result) => {
|
|
208
|
+
if (result) {
|
|
209
|
+
setEntries(entries().map((e) => (e.id === result.accessId ? { ...e, permission: result.permission } : e)));
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
onError: (err) => prompts.error(err.message),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const revokeMut = mutation.create<string | null, AccessEntry>({
|
|
216
|
+
mutation: async (entry) => {
|
|
217
|
+
const displayName = getEntryDisplayName(entry, t());
|
|
218
|
+
const confirmed = await prompts.confirm(t().removeAccessConfirm({ name: displayName }), {
|
|
219
|
+
title: t().removeAccess,
|
|
220
|
+
variant: "danger",
|
|
221
|
+
});
|
|
222
|
+
if (!confirmed) return null;
|
|
223
|
+
await props.revokeAccess(entry.id);
|
|
224
|
+
return entry.id;
|
|
225
|
+
},
|
|
226
|
+
onSuccess: (accessId) => {
|
|
227
|
+
if (accessId) setEntries(entries().filter((entry) => entry.id !== accessId));
|
|
228
|
+
},
|
|
229
|
+
onError: (err) => prompts.error(err.message),
|
|
230
|
+
});
|
|
231
|
+
const busy = () => grantMut.loading() || updateMut.loading() || revokeMut.loading();
|
|
232
|
+
|
|
233
|
+
// ── Combobox add-flow ─────────────────────────────────────────────────
|
|
234
|
+
// The Combobox is a fire-and-forget input: type → pick → granted at the
|
|
235
|
+
// lowest allowed level. The `principalsByOptId` map carries the original
|
|
236
|
+
// discriminated principal across the ComboboxOption boundary so onSelect
|
|
237
|
+
// can route it to grantAccess without re-parsing prefixed ids.
|
|
238
|
+
let principalsByOptId = new Map<string, Principal>();
|
|
239
|
+
|
|
240
|
+
const fetchPrincipals = async (q: string, signal: AbortSignal): Promise<ComboboxOption[]> => {
|
|
241
|
+
const map = new Map<string, Principal>();
|
|
242
|
+
const opts: ComboboxOption[] = [];
|
|
243
|
+
|
|
244
|
+
// Synthetic principals — only when allowed AND not already granted.
|
|
245
|
+
// Placed first so they're visible immediately on focus, before any
|
|
246
|
+
// typing kicks off a backend request.
|
|
247
|
+
if (allowAuthenticated() && !hasAuthenticatedEntry()) {
|
|
248
|
+
map.set("auth", { type: "authenticated" });
|
|
249
|
+
opts.push({
|
|
250
|
+
id: "auth",
|
|
251
|
+
label: t().allUsers,
|
|
252
|
+
description: t().signedInDescription,
|
|
253
|
+
icon: "ti-lock-open-2",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (allowPublic() && !hasPublicEntry()) {
|
|
257
|
+
map.set("public", { type: "public" });
|
|
258
|
+
opts.push({
|
|
259
|
+
id: "public",
|
|
260
|
+
label: t().public,
|
|
261
|
+
description: t().publicDescription,
|
|
262
|
+
icon: "ti-world",
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Real entities require a query — avoid a wide listing on every focus.
|
|
267
|
+
if (q.length >= 2) {
|
|
268
|
+
const url = new URL("/api/accounts/entities", window.location.origin);
|
|
269
|
+
url.searchParams.set("search", q);
|
|
270
|
+
url.searchParams.set("kinds", props.allowServiceAccounts ? "user,group,service_account" : "user,group");
|
|
271
|
+
url.searchParams.set("per_page", "10");
|
|
272
|
+
const userIds = existingUserIds();
|
|
273
|
+
if (userIds.length) url.searchParams.set("exclude_user_ids", userIds.join(","));
|
|
274
|
+
const groupIds = existingGroupIds();
|
|
275
|
+
if (groupIds.length) url.searchParams.set("exclude_group_ids", groupIds.join(","));
|
|
276
|
+
const serviceAccountIds = existingServiceAccountIds();
|
|
277
|
+
if (serviceAccountIds.length) url.searchParams.set("exclude_service_account_ids", serviceAccountIds.join(","));
|
|
278
|
+
|
|
279
|
+
const res = await fetch(url.toString(), { credentials: "same-origin", signal });
|
|
280
|
+
if (res.ok) {
|
|
281
|
+
const data = (await res.json()) as { items?: ApiEntity[] };
|
|
282
|
+
for (const item of data.items ?? []) {
|
|
283
|
+
if (item.kind === "user") {
|
|
284
|
+
const id = `u:${item.user.id}`;
|
|
285
|
+
map.set(id, { type: "user", userId: item.user.id });
|
|
286
|
+
opts.push({
|
|
287
|
+
id,
|
|
288
|
+
label: item.user.displayName,
|
|
289
|
+
description: item.user.mail ?? item.user.uid,
|
|
290
|
+
icon: "ti ti-user",
|
|
291
|
+
});
|
|
292
|
+
} else if (item.kind === "group") {
|
|
293
|
+
const id = `g:${item.group.id}`;
|
|
294
|
+
map.set(id, { type: "group", groupId: item.group.id });
|
|
295
|
+
opts.push({
|
|
296
|
+
id,
|
|
297
|
+
label: item.group.name,
|
|
298
|
+
description: item.group.description ?? undefined,
|
|
299
|
+
icon: "ti ti-users-group",
|
|
300
|
+
});
|
|
301
|
+
} else if (item.kind === "service_account") {
|
|
302
|
+
const id = `sa:${item.serviceAccount.id}`;
|
|
303
|
+
map.set(id, { type: "service_account", serviceAccountId: item.serviceAccount.id });
|
|
304
|
+
opts.push({
|
|
305
|
+
id,
|
|
306
|
+
label: item.serviceAccount.name,
|
|
307
|
+
description:
|
|
308
|
+
item.serviceAccount.kind === "user_delegated"
|
|
309
|
+
? t().userBoundServiceAccount
|
|
310
|
+
: [item.serviceAccount.appId, item.serviceAccount.resourceType, item.serviceAccount.resourceId]
|
|
311
|
+
.filter(Boolean)
|
|
312
|
+
.join(" · "),
|
|
313
|
+
icon: "ti ti-key",
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
principalsByOptId = map;
|
|
321
|
+
return opts;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const handleSelect = (option: ComboboxOption) => {
|
|
325
|
+
if (busy()) return;
|
|
326
|
+
const principal = principalsByOptId.get(option.id);
|
|
327
|
+
if (!principal) return;
|
|
328
|
+
const firstLevel = allowed()[0]?.level;
|
|
329
|
+
if (!firstLevel) return; // dev-warned above; bail silently
|
|
330
|
+
grantMut.mutate({ principal, permission: firstLevel, display: { displayName: option.label } });
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<div class="flex flex-col gap-3">
|
|
335
|
+
{/* Existing entries */}
|
|
336
|
+
<div class="flex flex-col gap-1">
|
|
337
|
+
<For each={entries()}>
|
|
338
|
+
{(entry) => (
|
|
339
|
+
<AccessEntryRow
|
|
340
|
+
entry={entry}
|
|
341
|
+
canEdit={canEdit()}
|
|
342
|
+
disabled={busy()}
|
|
343
|
+
allowed={allowed()}
|
|
344
|
+
singlePicker={isSinglePicker()}
|
|
345
|
+
onUpdatePermission={(permission) => {
|
|
346
|
+
if (!busy()) void updateMut.mutate({ accessId: entry.id, permission });
|
|
347
|
+
}}
|
|
348
|
+
onRevoke={() => {
|
|
349
|
+
if (!busy()) void revokeMut.mutate(entry);
|
|
350
|
+
}}
|
|
351
|
+
/>
|
|
352
|
+
)}
|
|
353
|
+
</For>
|
|
354
|
+
<Show when={entries().length === 0}>
|
|
355
|
+
<Placeholder align="left" class="px-1 py-2" description={t().noDirectGrants} />
|
|
356
|
+
</Show>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
{/* Add access — single Combobox, granted at the lowest allowed
|
|
360
|
+
level on pick. The user upgrades via the row pill if they want
|
|
361
|
+
a higher level. KISS: one decision per step. */}
|
|
362
|
+
<Show when={canEdit()}>
|
|
363
|
+
<Combobox
|
|
364
|
+
placeholder={
|
|
365
|
+
props.allowServiceAccounts
|
|
366
|
+
? allowAuthenticated()
|
|
367
|
+
? t().addAll
|
|
368
|
+
: t().addService
|
|
369
|
+
: allowAuthenticated()
|
|
370
|
+
? t().addAudience
|
|
371
|
+
: t().addBasic
|
|
372
|
+
}
|
|
373
|
+
fetchData={fetchPrincipals}
|
|
374
|
+
onSelect={handleSelect}
|
|
375
|
+
disabled={busy()}
|
|
376
|
+
/>
|
|
377
|
+
</Show>
|
|
378
|
+
</div>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
383
|
+
// Access Entry Row
|
|
384
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
385
|
+
|
|
386
|
+
function AccessEntryRow(props: {
|
|
387
|
+
entry: AccessEntry;
|
|
388
|
+
canEdit: boolean;
|
|
389
|
+
disabled: boolean;
|
|
390
|
+
allowed: ResolvedLevel[];
|
|
391
|
+
/** When true the per-row picker collapses to a non-interactive badge
|
|
392
|
+
* (single-level mode — there's nothing to switch to). */
|
|
393
|
+
singlePicker: boolean;
|
|
394
|
+
onUpdatePermission: (permission: GrantableLevel) => void;
|
|
395
|
+
onRevoke: () => void;
|
|
396
|
+
}) {
|
|
397
|
+
const locale = useLocale();
|
|
398
|
+
const t = () => accessMessages.resolve([locale()]).t;
|
|
399
|
+
const displayName = () => getEntryDisplayName(props.entry, t());
|
|
400
|
+
const display = () => resolveEntryDisplay(props.entry.permission, props.allowed, t());
|
|
401
|
+
const isInteractive = () =>
|
|
402
|
+
props.canEdit && !props.disabled && !props.singlePicker && props.allowed.some((option) => option.level === props.entry.permission);
|
|
403
|
+
|
|
404
|
+
const badgeClass =
|
|
405
|
+
"flex min-h-7 items-center gap-1 rounded-full border border-transparent bg-[var(--ui-surface-muted)] px-2.5 py-1 text-xs text-secondary";
|
|
406
|
+
|
|
407
|
+
const badgeContent = (
|
|
408
|
+
<>
|
|
409
|
+
<i class={`ti ${display().icon}`} />
|
|
410
|
+
<span>{display().label}</span>
|
|
411
|
+
<Show when={isInteractive()}>
|
|
412
|
+
<i class="ti ti-chevron-down text-[10px]" />
|
|
413
|
+
</Show>
|
|
414
|
+
</>
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<div class="group/access-row flex items-center gap-2 py-1.5">
|
|
419
|
+
<Show
|
|
420
|
+
when={props.entry.principal.type === "user"}
|
|
421
|
+
fallback={
|
|
422
|
+
<div class="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">
|
|
423
|
+
<i class={`ti ${getPrincipalIcon(props.entry.principal)} text-sm`} />
|
|
424
|
+
</div>
|
|
425
|
+
}
|
|
426
|
+
>
|
|
427
|
+
<CloudAvatar
|
|
428
|
+
username={displayName()}
|
|
429
|
+
userId={props.entry.principal.type === "user" ? props.entry.principal.userId : undefined}
|
|
430
|
+
avatarHash={props.entry.avatarHash}
|
|
431
|
+
size="xs"
|
|
432
|
+
class="h-7 w-7"
|
|
433
|
+
/>
|
|
434
|
+
</Show>
|
|
435
|
+
|
|
436
|
+
{/* Display name */}
|
|
437
|
+
<div class="min-w-0 flex-1">
|
|
438
|
+
<span class="truncate text-sm">{displayName()}</span>
|
|
439
|
+
<Show when={props.entry.principal.type === "public"}>
|
|
440
|
+
<span class="ml-1 text-xs text-dimmed">({t().anyoneWithLink})</span>
|
|
441
|
+
</Show>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
444
|
+
{/* Permission badge — interactive single-value picker when editable,
|
|
445
|
+
plain span otherwise. */}
|
|
446
|
+
<Show when={isInteractive()} fallback={<span class={`${badgeClass} cursor-default`}>{badgeContent}</span>}>
|
|
447
|
+
<SelectChip
|
|
448
|
+
aria-label={t().permissionFor({ name: displayName() })}
|
|
449
|
+
value={() => props.entry.permission as GrantableLevel}
|
|
450
|
+
options={props.allowed.map((option) => ({
|
|
451
|
+
value: option.level,
|
|
452
|
+
label: option.label,
|
|
453
|
+
icon: `ti ${option.icon}`,
|
|
454
|
+
}))}
|
|
455
|
+
icon={`ti ${display().icon}`}
|
|
456
|
+
position="bottom-left"
|
|
457
|
+
onValueChange={(permission) => {
|
|
458
|
+
if (permission !== props.entry.permission) props.onUpdatePermission(permission);
|
|
459
|
+
}}
|
|
460
|
+
/>
|
|
461
|
+
</Show>
|
|
462
|
+
|
|
463
|
+
{/* Destructive row actions stay quiet until the row is engaged. They
|
|
464
|
+
remain keyboard reachable and stay visible on touch-sized layouts. */}
|
|
465
|
+
<Show when={props.canEdit}>
|
|
466
|
+
<Tooltip.Anchor
|
|
467
|
+
content={t().remove({ name: displayName() })}
|
|
468
|
+
class="shrink-0 opacity-100 transition-opacity sm:opacity-0 sm:group-hover/access-row:opacity-100 sm:group-focus-within/access-row:opacity-100"
|
|
469
|
+
>
|
|
470
|
+
<IconButton
|
|
471
|
+
type="button"
|
|
472
|
+
label={t().remove({ name: displayName() })}
|
|
473
|
+
variant="ghost"
|
|
474
|
+
size="xs"
|
|
475
|
+
onClick={props.onRevoke}
|
|
476
|
+
disabled={props.disabled}
|
|
477
|
+
aria-label={t().remove({ name: displayName() })}
|
|
478
|
+
class="focus-ui flex h-7 w-7 items-center justify-center rounded text-zinc-400 transition-colors hover:bg-red-500/[0.08] hover:text-red-600 focus:opacity-100 dark:hover:text-red-400"
|
|
479
|
+
>
|
|
480
|
+
<i class="ti ti-x text-sm" />
|
|
481
|
+
</IconButton>
|
|
482
|
+
</Tooltip.Anchor>
|
|
483
|
+
</Show>
|
|
484
|
+
</div>
|
|
485
|
+
);
|
|
486
|
+
}
|